Course contents

groupingBy and partitioningBy in depth

groupingBy overloads, downstream collector combinations, map factories, groupingByConcurrent, null classifier behavior and partitioningBy rules.

PremiumMust know for interviewsLesson11 min readCoreReviewed Oct 2026 · Java 21
On this page
  1. Picture it first: chutes with different machines inside
  2. The three groupingBy overloads under the hood
  3. Downstream combinations that show real depth
  4. Map factories: TreeMap, EnumMap and ordering contracts
  5. Multi-level grouping and composite keys
  6. Null classifier results and concurrent grouping
  7. partitioningBy: the boolean case with both keys
  8. How to say it in the interview

🗂️ What are

groupingBy and partitioningBy? groupingBy is the collector that turns a stream into a map of buckets chosen by a classifier, such as customer, status or invoice month. partitioningBy is the boolean version: one bucket for true and one for false. It acts like a shipping sorter that reads each package label, chooses the chute, and lets a counter, set builder or max tracker inside that chute update the bucket.

For this lesson, we'll focus on the Collector API side: the three groupingBy overloads, map factories, downstream combinations, concurrent grouping, null classifier behavior and partitioningBy details. The collections module already teaches the broader grouping pattern in Grouping and partitioning, including manual computeIfAbsent. Here we'll use different order-report examples and go deeper into collector composition.

Below the line (out of scope)

The 30-second answer

groupingBy(classifier) produces a Map<K, List<T>>. groupingBy(classifier, downstream) keeps the same keys but lets each bucket publish something else, such as a count, sum, average, set, best element or nested map. groupingBy(classifier, mapFactory, downstream) also chooses the map implementation, for example TreeMap::new for sorted keys or () -> new EnumMap<>(Status.class) for enum keys. The default result map type and key order are not guaranteed, classifier results cannot be null, and groupingByConcurrent is for concurrent maps with parallel-friendly downstream rules. partitioningBy(predicate) is the boolean case and always includes both true and false keys, even when one side is empty.

What you’ll learn

  • Use all three **groupingBy overloads**: classifier, classifier plus downstream, and classifier plus map factory plus downstream
  • Combine downstream collectors like counting, summingInt, mapping, maxBy, collectingAndThen, averaging and nested groupingBy
  • Choose **TreeMap and EnumMap** when result map ordering or enum keys are part of the contract
  • Group orders by customer, status, month and record composite keys without losing type safety
  • Explain groupingByConcurrent, null classifier NullPointerException, and default map ordering limits
  • Use partitioningBy when a boolean split should publish both true and false keys

01Picture it first: chutes with different machines inside

Picture a fulfillment center with three sorting chutes. The label reader sends each order to a customer, status or month chute. Inside each chute, you choose the machine: a tray for orders, a counter, a revenue meter, a set of SKUs, or a max tracker.

The classifier and downstream collector are separate decisions. The classifier chooses the map key; the downstream chooses the value type. Once that clicks, the overloads read like a small language for reports.

Fig. 1

Orders flow through classifiers for customer, status and month, then downstream collectors turn each bucket into lists, counts, totals or best orders

Orders flow through classifiers for customer, status and month, then downstream collectors turn each bucket into lists, counts, totals or best orders

Notice the map factory sitting outside the buckets. It does not change which orders belong together. It changes the map that holds the groups, which is why ordering requirements belong in the overload choice.

02The three groupingBy overloads under the hood

The one-argument overload is the default shape: Map<K, List<T>>. Use it when the caller really needs the original objects by key. Do not use it as a habit when the caller only needs a count or total.

Try it yourself first

Before reading on, say the result type of each collection: orders by customer, count by status, and revenue by month in a sorted map.

import java.time.YearMonth;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;

enum Status { NEW, PAID, SHIPPED, CANCELLED }
record Order(String id, String customerId, Status status, YearMonth month, int cents) { }

public class GroupingOverloadsDemo {
    public static void main(String[] args) {
        List<Order> orders = List.of(
                new Order("o-1", "c-2", Status.PAID, YearMonth.of(2026, 10), 1200),
                new Order("o-2", "c-1", Status.NEW, YearMonth.of(2026, 10), 700),
                new Order("o-3", "c-2", Status.PAID, YearMonth.of(2026, 11), 400));

        Map<String, List<Order>> byCustomer = orders.stream()
                .collect(Collectors.groupingBy(Order::customerId));

        Map<Status, Long> countByStatus = orders.stream()
                .collect(Collectors.groupingBy(Order::status, Collectors.counting()));

        Map<YearMonth, Integer> revenueByMonth = orders.stream()
                .collect(Collectors.groupingBy(
                        Order::month,
                        TreeMap::new,
                        Collectors.summingInt(Order::cents)));

        System.out.println(byCustomer.get("c-2").size());
        System.out.println(countByStatus.get(Status.PAID));
        System.out.println(revenueByMonth.keySet());
    }
}

Notice three things:

  • The one-argument overload gives lists because no downstream collector was supplied.
  • The two-argument overload changes each bucket from List<Order> to Long.
  • The three-argument overload makes ordering part of the contract by using TreeMap::new.

If you use the one- or two-argument overload, do not promise HashMap ordering to callers. The API does not guarantee the returned map type. The common implementation may be a HashMap, but tests and APIs should not depend on that.

Premium lesson

Keep reading with the Java Pass

The rest of this lesson is part of Java One-Stop Interview Prep: in-depth explanations, diagrams, model answers and how to say them in the interview.

  • Downstream combinations that show real depth
  • Map factories: TreeMap, EnumMap and ordering contracts
  • Multi-level grouping and composite keys
  • Null classifier results and concurrent grouping
  • partitioningBy: the boolean case with both keys
  • How to say it in the interview

From ₹199 · one-time payment · No auto-renewal

Practise this topicCollectors: interview questions & scenarios
Further reading (5)