Collectors.toMap
`Collectors.toMap` is a Java Stream collector that builds a `Map` from stream elements. When multiple elements can produce the same key, you must provide a merge strategy or the collection can fail with `IllegalStateException`.
The basic form is `Collectors.toMap(keyMapper, valueMapper)`. The first function determines each map key, while the second determines the corresponding value. For example, a stream of users can be converted into a map keyed by user ID.
The two-argument overload assumes that generated keys are unique. If two elements produce the same key, the collector cannot choose a value automatically and throws `IllegalStateException` rather than silently replacing an existing entry.
When duplicate keys are possible, use the three-argument overload: `toMap(keyMapper, valueMapper, mergeFunction)`. The merge function receives the existing value and the new value and returns the value that should remain in the map.
A fourth argument can supply a `mapFactory`, allowing you to choose the resulting map implementation, such as `LinkedHashMap` when iteration order matters. The chosen map implementation and its characteristics should therefore be considered when designing the collection operation.
Key points
- `toMap` transforms stream elements into key-value entries.
- The two-argument overload requires unique generated keys.
- Use a merge function when duplicate keys are possible.
- A map supplier can select the concrete `Map` implementation.
- Duplicate-key handling should be explicit rather than accidental.
Interview questions
What is the purpose of the fourth argument to `Collectors.toMap`?
The fourth argument is a map supplier that controls the concrete map implementation used for the result. For example, `LinkedHashMap::new` can be supplied when insertion-order iteration is required. This is useful when the default map implementation is not appropriate for the application's requirements.
What does `Collectors.toMap` do?
It collects stream elements into a `Map`. A key-mapping function determines the key and a value-mapping function determines the value. The basic overload expects the generated keys to be unique. If duplicate keys occur, collection fails rather than choosing a value implicitly.
How do you handle duplicate keys with `toMap`?
Use the three-argument overload and provide a merge function. The merge function receives the existing mapped value and the value produced by the later element. It returns the value that should be stored for that key. For example, `Integer::sum` can aggregate duplicate values instead of rejecting them.
How would you collect objects by ID while handling duplicate IDs?
I would map each object to its ID and itself, then provide a merge function that defines what duplicate IDs mean. If the newest object should win, the merge function can return the second value; if the first should win, it can return the first value. The important part is making the business rule explicit rather than relying on accidental behavior.
What should you consider when using `toMap` in a parallel stream?
The collector must combine partial results correctly, so the merge behavior matters when multiple stream partitions produce the same key. A merge function should be associative enough for the intended aggregation, and code should not depend on an encounter order that the parallel operation does not guarantee. Performance and the characteristics of the supplied map implementation also matter. If ordering or deterministic conflict resolution is essential, I would examine whether parallel collection is appropriate.