Functional Programming & Streams
Collectors
collect performs mutable reduction through a Collector recipe. Collectors covers containers (toList, toSet, toMap), strings (joining), statistics, and — its real power — groupingBy/partitioningBy with downstream collectors.toMap(keyFn, valueFn)throws on duplicate keys — pass a merge function when keys can repeatgroupingBy(classifier)→Map<K, List<T>>; add a downstream collector to aggregate per grouppartitioningBy(predicate)→Map<Boolean, …>with exactly two entries- Downstream combinators:
counting,summingInt,mapping,filtering,collectingAndThen joining(", ", "[", "]")for delimited strings
Map<String, Employee> byId = staff.stream()
.collect(toMap(Employee::id, e -> e)); // throws on dup ids — good!
Map<String, Double> highestPayByDept = staff.stream()
.collect(toMap(Employee::dept, Employee::salary, Double::max)); // merge fn
String csv = names.stream().collect(joining(", "));
IntSummaryStatistics stats = words.stream().collect(summarizingInt(String::length));
// stats.getMax(), getMin(), getAverage(), getCount(), getSum()Map<String, List<Employee>> byDept = staff.stream()
.collect(groupingBy(Employee::dept));
Map<String, Long> headcount = staff.stream()
.collect(groupingBy(Employee::dept, counting()));
Map<String, Double> payroll = staff.stream()
.collect(groupingBy(Employee::dept, summingDouble(Employee::salary)));
Map<String, Set<String>> namesByDept = staff.stream()
.collect(groupingBy(Employee::dept,
mapping(Employee::name, toCollection(TreeSet::new))));Downstream collectors nest arbitrarily — groupingBy(f, groupingBy(g, counting())) builds two-level maps. collectingAndThen(toList(), List::copyOf) post-processes a result (here: seal it immutable). teeing(c1, c2, merger) (Java 12) runs two collectors over one pass and merges their results.