Collectors
The Collectors class, in java.util.stream, is a utility class of static factory methods that produce Collector instances — the objects that .collect() uses to gather a stream's elements into a final result. Almost every real-world stream pipeline ends with .collect(Collectors.someMethod()).
Common collectors
Collector | Produces |
|---|---|
| A |
| A |
| A |
| A single |
| A |
| A |
| A summed or averaged |
Basic collectors in action
toList, toSet, joining, and toMap
List<String> names = List.of("Alice", "Bob", "Charlie", "Dave");
List<String> asList = names.stream()
.filter(n -> n.length() > 3)
.collect(Collectors.toList());
// [Alice, Charlie, Dave]
String joined = names.stream()
.collect(Collectors.joining(", ", "[", "]"));
// [Alice, Bob, Charlie, Dave]
Map<String, Integer> nameLengths = names.stream()
.collect(Collectors.toMap(name -> name, String::length));
// {Alice=5, Bob=3, Charlie=7, Dave=4}A worked example: groupingBy
Collectors.groupingBy() is one of the most useful, commonly-reached-for collectors in real code — it buckets stream elements into a Map keyed by whatever “classifier” function you give it, with each bucket holding a List of the elements that produced that key.
Grouping employees by department
record Employee(String name, String department, double salary) {}
List<Employee> employees = List.of(
new Employee("Alice", "Engineering", 95000),
new Employee("Bob", "Sales", 62000),
new Employee("Charlie", "Engineering", 110000),
new Employee("Dave", "Sales", 58000),
new Employee("Eve", "Marketing", 71000)
);
Map<String, List<Employee>> byDepartment = employees.stream()
.collect(Collectors.groupingBy(Employee::department));
// {Engineering=[Alice, Charlie], Sales=[Bob, Dave], Marketing=[Eve]}
byDepartment.forEach((dept, staff) ->
System.out.println(dept + ": " + staff.size() + " employees"));groupingBy also accepts a second, downstream collector, which processes each group's elements further instead of just collecting them into a List — for example, counting each group or summing a field.
groupingBy with a downstream collector
Map<String, Long> countByDepartment = employees.stream()
.collect(Collectors.groupingBy(Employee::department, Collectors.counting()));
// {Engineering=2, Sales=2, Marketing=1}
Map<String, Double> totalSalaryByDepartment = employees.stream()
.collect(Collectors.groupingBy(Employee::department, Collectors.summingDouble(Employee::salary)));
// {Engineering=205000.0, Sales=120000.0, Marketing=71000.0}Collectorsis a factory class ofCollectorimplementations, used with.collect().toList(),toSet(),toMap(), andjoining()cover the most common simple conversions.groupingBy()buckets elements by a classifier function into aMapofLists — a very common real-world pattern.A second, downstream collector can be passed to
groupingBy()to reduce each group instead of collecting it as aList.