BackendBench
Coffee

Unravel โ€บ Java concepts โ€บ Collections and streams

Java language

Collectors: groupingBy, toMap, joining

A Collector decides what a stream becomes at the end: a list, a map, a group per key, a count, a joined string.

Learn these first

At a glance

Mental model

Sorting a pile of mail into pigeonholes: the collector says which pigeonhole each letter goes into and what you do with the letters inside (count them, list them, add up the postage).

What happens, in order
  1. 1stream.collect(Collectors.groupingBy(Order::customer)) gives Map<Customer, List<Order>>A second argument decides what to do with each group: counting(), summingInt(Order::qty), mapping(Order::id, toList()).
  2. 2partitioningBy(x -> x > 10) splits into exactly two groups (true and false)
  3. 3toMap(keyFn, valueFn) throws IllegalStateException on duplicate keys unless you pass a merge function as a third argument: toMap(k, v, Integer::sum)
  4. 4joining(", ", "[", "]") builds a string, averagingInt computes means, summarizingInt gives count, sum, min, max and average in one pass, teeing runs two collectors and combines the results
Remember
Watch out
toMap without a merge function when keys can repeatNull keys in groupingByTreating the resulting map as ordered
Example (highlighted lines explain it)
import java.util.*;import java.util.stream.*;public class Reports {  record Order(String customer, int qty, double total) {}  static Map<String, Double> totalPerCustomer(List<Order> orders) {    return orders.stream().collect(Collectors.groupingBy(Order::customer, TreeMap::new, Collectors.summingDouble(Order::total)));  }  static Map<Boolean, Long> bigVsSmall(List<Order> orders) {    return orders.stream().collect(Collectors.partitioningBy(o -> o.qty() > 10, Collectors.counting()));  }  static Map<String, Integer> qty(List<Order> orders) {    return orders.stream().collect(Collectors.toMap(Order::customer, Order::qty, Integer::sum));  }}
  1. line 6 uses groupingBy, the core of Collectors: groupingBy, toMap, joining
  2. line 9 uses partitioningBy, the core of Collectors: groupingBy, toMap, joining
  3. line 12 uses toMap, the core of Collectors: groupingBy, toMap, joining
Read the full explanation (about 1 min)

A Collector decides what a stream becomes at the end: a list, a map, a group per key, a count, a joined string. groupingBy is the stream version of SQL's GROUP BY.

A mental model

Sorting a pile of mail into pigeonholes: the collector says which pigeonhole each letter goes into and what you do with the letters inside (count them, list them, add up the postage).

How it works

stream.collect(Collectors.groupingBy(Order::customer)) gives Map<Customer, List<Order>>. A second argument decides what to do with each group: counting(), summingInt(Order::qty), mapping(Order::id, toList()).

partitioningBy(x -> x > 10) splits into exactly two groups (true and false).

toMap(keyFn, valueFn) throws IllegalStateException on duplicate keys unless you pass a merge function as a third argument: toMap(k, v, Integer::sum).

joining(", ", "[", "]") builds a string, averagingInt computes means, summarizingInt gives count, sum, min, max and average in one pass, teeing runs two collectors and combines the results.

Going deeper

groupingBy returns a HashMap by default; pass a map factory (TreeMap::new) for sorted keys. groupingBy rejects null keys (NullPointerException).

Many aggregations are better in the database (GROUP BY returns less data). Use stream grouping for data already in memory.

Downstream collectors compose, so "total per customer per month" is a groupingBy inside a groupingBy. Keep nesting to two levels or name the pieces.

Under the hood

A collector is made of four functions: supplier (create the container), accumulator (add one element), combiner (merge two partial containers, used by parallel streams) and finisher. groupingBy builds a map whose values are the downstream collectors' containers.

Common mistakes

  • toMap without a merge function when keys can repeat.
  • Null keys in groupingBy.
  • Treating the resulting map as ordered.

How Unravel recognises it

When you paste code, Unravel looks for these signs of collectors: groupingby, tomap, joining. Spotting them yourself is a good habit when you read unfamiliar code:

Part of a learning path

Related concepts

Frequently asked questions

What is Collectors: groupingBy, toMap, joining?

A Collector decides what a stream becomes at the end: a list, a map, a group per key, a count, a joined string. groupingBy is the stream version of SQL's GROUP BY.

What are the most common mistakes with Collectors: groupingBy, toMap, joining?

toMap without a merge function when keys can repeat. Null keys in groupingBy. Treating the resulting map as ordered.

How can I see Collectors: groupingBy, toMap, joining in real code?

Paste your own Java into Unravel and it will point out where collectors: groupingby, tomap, joining appears, explain how it works at the detail level you choose, and draw the flow of the code around it.