Collectors Class in Java
The Collectors class in Java, in java.util.stream, is a utility class full of static methods that create collectors. A collector tells the terminal operation collect how to gather the elements of a stream into a result: a list, a set, a map, a string, a number or a group. In this lesson, you will learn the most useful collectors with examples. Grouping and partitioning are covered in detail in "groupingBy, partitioningBy, toMap and Joining".
Syntax
import static java.util.stream.Collectors.*;
R result = stream.collect(someCollector());Methods of Collectors
| Method | Description |
|---|---|
toList() | It collects into a List. |
toSet() | It collects into a Set. |
toCollection(TreeSet::new) | It collects into a collection of your choice. |
toUnmodifiableList() / toUnmodifiableSet() | It collects into an unmodifiable list or set. |
toMap(keyFn, valueFn) | It collects into a Map. |
joining(", ") | It joins strings with a separator. |
counting() | It counts the elements. |
summingInt(f) / averagingInt(f) | It totals or averages a number taken from each element. |
minBy(c) / maxBy(c) | It finds the smallest or largest by a comparator. |
summarizingInt(f) | It gives count, sum, min, average and max. |
mapping(f, downstream) | It transforms elements before another collector. |
filtering(p, downstream) | It filters elements before another collector (Java 9). |
groupingBy(f) | It groups elements into a map of lists. |
partitioningBy(p) | It splits elements into a true group and a false group. |
teeing(c1, c2, merger) | It runs two collectors and merges their results (Java 12). |
collectingAndThen(c, f) | It applies a final function to a collector's result. |
Example: Collecting into Collections and Strings
Let's see an example of the basic collectors.
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
public class BasicCollectors {
public static void main(String[] args) {
List<String> names = List.of("Srihitha", "Navika", "Advitha", "Navika");
List<String> list = names.stream().collect(Collectors.toList());
Set<String> set = names.stream().collect(Collectors.toSet());
TreeSet<String> sorted = names.stream()
.collect(Collectors.toCollection(TreeSet::new));
String joined = names.stream().distinct()
.collect(Collectors.joining(", ", "[", "]"));
System.out.println(list.size() + " in list, " + set.size() + " in set");
System.out.println(sorted);
System.out.println(joined);
}
}4 in list, 3 in set
[Advitha, Navika, Srihitha]
[Srihitha, Navika, Advitha]Explanation: toList keeps all four names, while toSet drops the duplicate Navika. toCollection(TreeSet::new) chooses the exact collection type. joining(", ", "[", "]") adds a separator, a prefix and a suffix.
Example: Counting, Summing and Averaging
import java.util.Comparator;
import java.util.IntSummaryStatistics;
import java.util.List;
import java.util.stream.Collectors;
public class NumberCollectors {
record Student(String name, int marks) { }
public static void main(String[] args) {
List<Student> students = List.of(
new Student("Navika", 91), new Student("Srihitha", 78),
new Student("Advitha", 85));
long count = students.stream().collect(Collectors.counting());
int total = students.stream()
.collect(Collectors.summingInt(Student::marks));
double average = students.stream()
.collect(Collectors.averagingInt(Student::marks));
String top = students.stream()
.collect(Collectors.maxBy(Comparator.comparingInt(Student::marks)))
.map(Student::name).orElse("none");
IntSummaryStatistics stats = students.stream()
.collect(Collectors.summarizingInt(Student::marks));
System.out.println(count + " students, total " + total
+ ", average " + average + ", top " + top);
System.out.println("min " + stats.getMin() + ", max " + stats.getMax());
}
}3 students, total 254, average 84.66666666666667, top Navika
min 78, max 91Explanation: These collectors produce single values. On their own, count(), mapToInt(...).sum() and max(...) are simpler; the collectors really shine as downstream collectors inside groupingBy, where they compute a value per group.
Example: teeing
import java.util.List;
import java.util.stream.Collectors;
public class Teeing {
public static void main(String[] args) {
List<Integer> marks = List.of(91, 78, 85, 66);
String range = marks.stream().collect(Collectors.teeing(
Collectors.minBy(Integer::compare),
Collectors.maxBy(Integer::compare),
(min, max) -> min.get() + " to " + max.get()));
System.out.println("Range: " + range);
}
}Range: 66 to 91Explanation: teeing sends every element to two collectors at once, here one for the minimum and one for the maximum, and then merges the two results with a function.
Common Mistakes
Assuming Collectors.toList() returns an unmodifiable list. It doesn't guarantee either way; stream.toList() is unmodifiable.
Using toSet() and expecting order. It's usually a HashSet; use toCollection(TreeSet::new) or LinkedHashSet::new.
Using collectors for simple totals. mapToInt(...).sum() is clearer than collect(summingInt(...)).
Interview Corner
What is the difference between stream.toList() and collect(Collectors.toList())?
stream.toList(), added in Java 16, always returns an unmodifiable list and allows null elements. collect(Collectors.toList()) returns a list whose type and mutability aren't guaranteed; in practice it is an ArrayList. Use toList() for read-only results, and Collectors.toCollection(ArrayList::new) when you need a list you can change.
Practice
- Easy: Collect a list of words into a comma-separated string of the words in upper case.
- Harder: Use
teeingto compute both the count of passing marks and the count of failing marks in one pipeline, and print them as "3 passed, 1 failed".
Key Takeaways
Was this tutorial helpful?