Card 06/ 10
Collectors: Ending a Stream in a List, a Set or a Map
Decide the shape you want out, and the collector is usually already written.
collect is the terminal operation (the last step, which produces a result and makes the stream run) that builds a collection from a stream, and the Collectors class supplies ready-made recipes, called collectors, for lists, sets, maps and strings.
Three pipelines ending in three shapes:
List<String> refs = invoices.stream().map(Invoice::reference).toList();
Set<String> unique = invoices.stream().map(Invoice::customer).collect(Collectors.toSet());
Map<String, Long> byCustomer =
invoices.stream().collect(Collectors.groupingBy(Invoice::customer, Collectors.counting()));Line 1 needs no collector at all, because toList() is a method on the stream itself. Lines 2 to 4 hand collect a recipe, and the recipe decides both the kind of collection and what happens to duplicates.
| Collector | Produces |
|---|---|
toList() on the stream | An unmodifiable list (adding to it throws), in encounter order |
Collectors.toSet() | A set, with duplicates gone and no promised order |
Collectors.toMap(key, value) | A map, throwing if two elements produce one key |
Collectors.groupingBy(key) | A map from key to a list of the elements with it |
Collectors.joining(", ") | One string, with a separator |
groupingBy is the one that replaces the most code. A second collector inside it says what to do with each group, so counting, summing and collecting to a set are all the same call with a different second argument.
So the end of a pipeline is a choice about shape rather than about work. Decide what you want out, and the collector is usually already written.
← → move through sections and cards