What are Collectors, and how do you use them for grouping/partitioning?

9 minintermediatecollectorsgroupingbystreams

Quick Answer

Collectors is a factory class providing common terminal 'reduction' strategies to use with Stream.collect() — toList/toSet/toMap for building collections, joining for strings, and groupingBy/partitioningBy for building maps that bucket elements by a classifier function or a boolean predicate, respectively, optionally with a downstream collector for further per-group aggregation.

Detailed Answer

Collectors provides ready-made implementations of Collector, the strategy object Stream.collect() uses to accumulate stream elements into a final result:

List<String> list = stream.collect(Collectors.toList());
Set<String> set = stream.collect(Collectors.toSet());
String joined = stream.collect(Collectors.joining(", ", "[", "]")); // "[a, b, c]"
Map<String, Integer> map = stream.collect(Collectors.toMap(Person::name, Person::age));

groupingBy buckets elements into a Map<K, List<T>> (by default) keyed by a classifier function:

Map<String, List<Person>> byCity = people.stream()
    .collect(Collectors.groupingBy(Person::city));

With a downstream collector, each bucket can be further reduced instead of collected into a raw list:

Map<String, Long> countByCity = people.stream()
    .collect(Collectors.groupingBy(Person::city, Collectors.counting()));

Map<String, Double> avgAgeByCity = people.stream()
    .collect(Collectors.groupingBy(Person::city, Collectors.averagingInt(Person::age)));

partitioningBy is a specialized two-bucket groupingBy that splits elements into exactly Map<Boolean, List<T>> based on a Predicate — always produces both true and false keys, even if one bucket is empty:

Map<Boolean, List<Person>> adults = people.stream()
    .collect(Collectors.partitioningBy(p -> p.age() >= 18));
List<Person> minors = adults.get(false);

When to use which: groupingBy for classifying into an arbitrary number of buckets by any key; partitioningBy specifically for a binary yes/no split, which is both more explicit about intent and slightly cheaper than groupingBy with a boolean classifier.