groupingBy and partitioningBy

Streams · lesson 9 of 42 · 4 min read

Build grouped reports in one pass with groupingBy, partitioningBy and downstream collectors.

Open this lesson in the learning hub

Key points

  • groupingBy(classifier) returns a Map of key to List of matching elements. It is the GROUP BY of Java.
  • The second argument is a downstream collector: counting(), summingInt(), mapping(), toSet(), maxBy().
  • Downstream collectors nest, so you can group by department, map to names, then join them.
  • partitioningBy(predicate) is a yes/no split. Both the true and false keys always exist, even when empty.
  • Pass a map factory such as TreeMap::new when key order matters.

Example

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

public class Main {
    record Employee(String name, String dept, int salary) {}

    public static void main(String[] args) {
        List<Employee> staff = List.of(
                new Employee("Ada", "eng", 120),
                new Employee("Alan", "eng", 110),
                new Employee("Grace", "eng", 130),
                new Employee("Edsger", "sales", 90),
                new Employee("Barbara", "sales", 95));

        Map<String, Long> headcount = staff.stream()
                .collect(Collectors.groupingBy(Employee::dept, Collectors.counting()));

        Map<String, Integer> payroll = staff.stream()
                .collect(Collectors.groupingBy(Employee::dept, Collectors.summingInt(Employee::salary)));

        Map<String, List<String>> namesByDept = staff.stream()
                .collect(Collectors.groupingBy(Employee::dept, TreeMap::new,
                        Collectors.mapping(Employee::name, Collectors.toList())));

        Map<Boolean, List<String>> split = staff.stream()
                .collect(Collectors.partitioningBy(e -> e.salary() >= 110,
                        Collectors.mapping(Employee::name, Collectors.toList())));

        Map<String, Optional<Employee>> topEarner = staff.stream()
                .collect(Collectors.groupingBy(Employee::dept,
                        Collectors.maxBy(Comparator.comparingInt(Employee::salary))));

        System.out.println("headcount : " + new TreeMap<>(headcount));
        System.out.println("payroll   : " + new TreeMap<>(payroll));
        System.out.println("names     : " + namesByDept);
        System.out.println("well paid : " + split.get(true));
        System.out.println("the rest  : " + split.get(false));
        System.out.println("top eng   : " + topEarner.get("eng").orElseThrow().name());
    }
}

groupingBy plus a downstream collector replaces most report-building loops.

This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Streams course, and every lesson in it is listed on the Streams contents page.