Collectors
A Collector describes how to assemble a result
Stream.toList() is great when a regular list is needed.
But sometimes elements must be joined into a string, grouped by a property,
or turned into a Map. For such tasks, the terminal operation
collect() is used together with ready-made collectors from the Collectors class.
joining combines strings
String result = Stream.of("Java", "Spring", "SQL")
.collect(Collectors.joining(", "));
The result value is Java, Spring, SQL.
joining also has variants with a prefix and suffix
when the final string should be formatted immediately.
groupingBy collects elements by key
groupingBy computes a key for each element
and places elements with the same key into one group.
Map<Integer, List<String>> byLength =
Stream.of("Java", "SQL", "Spring", "HTTP")
.collect(Collectors.groupingBy(String::length));
The string length becomes the key here. As a result, strings of length 4
are placed into one group, strings of length 3 into another, and so on.
The similar partitioningBy operation splits elements into exactly two groups
according to the result of a Predicate.
toMap builds a map
Map<String, Integer> lengths =
Stream.of("Java", "Spring", "SQL")
.collect(Collectors.toMap(
word -> word,
String::length
));
The first rule creates the key, and the second creates the value.
If two elements produce the same key, the simple toMap form
cannot choose a winner and throws an exception.
For such data, a merge function must be defined in advance.
Map<Integer, String> oneWordByLength =
Stream.of("Java", "HTTP", "Spring")
.collect(Collectors.toMap(
String::length,
word -> word,
(left, right) -> left
));
Stream.toList() and Collectors.toList() are not complete equivalents.
Stream.toList() returns an unmodifiable list.
Collectors.toList() does not guarantee a particular implementation or mutability of the result.
If a specific collection is required, it is better to express that explicitly.