Back to Stream: Grouping & Partitioning
Q21Question 21 of 22

Separate odd and even numbers from a list using partitioningBy

Key Concepts
  • partitioningBy(n -> n % 2 == 0) splits numbers into even (true) and odd (false) lists
  • partitioningBy always produces both keys, so even an empty side exists as an empty list
  • This guaranteed two-key result avoids null checks that groupingBy on a boolean could require
  • A downstream collector can be nested, for example counting() to get counts per parity
  • Use partitioningBy specifically for binary splits; use groupingBy for more than two categories
  • The result type is Map<Boolean, List<Integer>> with true for even and false for odd

Definition

Collectors.partitioningBy(n -> n % 2 == 0) splits a stream into even (true) and odd (false) lists, always producing both keys.
  • partitioningBy(predicate) divides elements into exactly two groups: true and false.
  • Always two keys: both true and false exist in the result, even if one side is empty.
  • Even predicate: n -> n % 2 == 0 classifies even numbers as true, odd as false.
  • Downstream nesting: add counting() to get counts per parity instead of full lists.
  • Result type: Map> for the default; changes with downstream.

Priya at the National Bank of India splits transaction IDs into two processing lanes: even-numbered ones go to lane A and odd-numbered ones go to lane B. Both lanes always exist, even if one batch happens to have no even IDs.

Understanding the Concepts

What does this question ask?

The interviewer wants you to separate odd and even numbers using partitioningBy. This tests the distinction between partitioning and grouping, the guaranteed two-key result, and downstream collector nesting.

How do you partition into odd and even?

JAVA
package com.bank.service;

import java.util.*;
import java.util.stream.Collectors;

public class OddEvenPartition {
    public static void main(String[] args) {
        List<Integer> transactionIds = List.of(
            1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008);

        Map<Boolean, List<Integer>> partition = transactionIds.stream()
            .collect(Collectors.partitioningBy(n -> n % 2 == 0));

        System.out.println("Even: " + partition.get(true));
        System.out.println("Odd:  " + partition.get(false));
    }
}
TEXT
Even: [1002, 1004, 1006, 1008]
Odd:  [1001, 1003, 1005, 1007]

Why partitioningBy instead of groupingBy on a boolean?

| Feature | partitioningBy | groupingBy(isEven) |
|---|---|---|
| Keys guaranteed | Both true and false always present | Missing key if no elements match |
| Null check needed | No | Yes, for missing key |
| Efficiency | Slightly optimized for binary split | General-purpose |

JAVA
// partitioningBy: both keys always exist
Map<Boolean, List<Integer>> partition = List.<Integer>of().stream()
    .collect(Collectors.partitioningBy(n -> n % 2 == 0));
System.out.println(partition.get(true));   // [] (empty list, not null)
System.out.println(partition.get(false));  // [] (empty list, not null)

// groupingBy on boolean: missing keys possible
Map<Boolean, List<Integer>> group = List.<Integer>of().stream()
    .collect(Collectors.groupingBy(n -> n % 2 == 0));
System.out.println(group.get(true));   // null!
System.out.println(group.get(false));  // null!

How do you nest a downstream collector?

JAVA
// Count per parity
Map<Boolean, Long> counts = transactionIds.stream()
    .collect(Collectors.partitioningBy(
        n -> n % 2 == 0,
        Collectors.counting()));

System.out.println("Even count: " + counts.get(true));
System.out.println("Odd count:  " + counts.get(false));
TEXT
Even count: 4
Odd count: 4

How do you use partitioning for pass/fail classification?

JAVA
package com.bank.service;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

public class ThresholdPartition {
    public static void main(String[] args) {
        List<BigDecimal> balances = List.of(
            new BigDecimal("50000"),
            new BigDecimal("125000"),
            new BigDecimal("75000"),
            new BigDecimal("25000"));

        BigDecimal threshold = new BigDecimal("50000");

        Map<Boolean, List<BigDecimal>> partition = balances.stream()
            .collect(Collectors.partitioningBy(
                b -> b.compareTo(threshold) >= 0));

        System.out.println("Above threshold: " + partition.get(true));
        System.out.println("Below threshold: " + partition.get(false));
    }
}
TEXT
Above threshold: [50000, 125000, 75000]
Below threshold: [25000]

What are common mistakes?

Mistake: using groupingBy when partitioningBy is more appropriate.

JAVA
// WRONG for binary split: groupingBy may have missing keys
Map<Boolean, List<Integer>> wrong = List.of(1, 3, 5).stream()
    .collect(Collectors.groupingBy(n -> n % 2 == 0));
List<Integer> evens = wrong.get(true);  // null!

// CORRECT: partitioningBy guarantees both keys
Map<Boolean, List<Integer>> correct = List.of(1, 3, 5).stream()
    .collect(Collectors.partitioningBy(n -> n % 2 == 0));
List<Integer> safeEvens = correct.get(true);  // [] (empty, not null)

Mistake: confusing the parity test direction.

JAVA
// Check which key holds evens vs odds
// n % 2 == 0 → true = even, false = odd
// n % 2 != 0 → true = odd, false = even (reversed!)

When should you use groupingBy instead?

Use groupingBy when there are more than two categories.

JAVA
// Three categories: negative, zero, positive
Map<String, List<Integer>> threeWay = List.of(-5, 0, 3, -2, 7).stream()
    .collect(Collectors.groupingBy(n ->
        n < 0 ? "negative" : n == 0 ? "zero" : "positive"));

What are common interview follow-up questions?

  • "How do you partition employees by salary above/below a threshold?" Same pattern with a salary comparison predicate.
  • "How do you get both the list and the count?" Use collectingAndThen or process the partition result.
  • "Can you partition by more than one condition?" Not directly; use groupingBy with a multi-valued classifier.

Visual Overview

DIAGRAM
  List: [1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008]
       │
       │  partitioningBy(n -> n % 2 == 0)
       ▼
  ┌───────────────────────────────────────────┐
  │  true  (even): [1002, 1004, 1006, 1008]  │
  │  false (odd):  [1001, 1003, 1005, 1007]  │
  └───────────────────────────────────────────┘
  Both keys always present, even if one side is empty

Key Takeaway

Separating odd and even numbers uses partitioningBy with an even predicate, which divides the stream into exactly two buckets keyed by true and false. The defining feature of partitioningBy is that it always produces both keys, so even if every number is odd, the true list still exists as an empty list.

That guarantee is why partitioningBy is preferred over groupingBy on a boolean for binary splits. groupingBy could omit an empty side and force a null check, while partitioningBy lets you safely read both lanes. A downstream collector like counting() can be nested for counts per side.

At the National Bank of India, Rajesh used this to split transaction IDs into even and odd for two parallel processing lanes, ensuring both lanes always have a valid (possibly empty) list.

Remember one sentence: partitioningBy always produces both true and false keys, making it the right tool for binary classification like odd/even.

---

Share
Use ← → arrow keys to navigate