Collectors.groupingBy

`Collectors.groupingBy` partitions stream elements into a `Map` according to a classification function. It is useful for turning a flat stream into groups such as employees by department or orders by status.

`groupingBy` takes a classifier function that determines the key for each stream element. The collector then accumulates elements sharing the same key into a collection, producing a `Map<K, List<T>>` with the common one-argument form.

For example, `employees.stream().collect(Collectors.groupingBy(Employee::department))` creates one map entry per department. Every employee classified as the same department is placed in that department's list.

The overloaded forms allow you to control the downstream aggregation and, in the three-argument form, the map implementation. A downstream collector can replace the default `List` accumulation with operations such as `counting()`, `mapping()`, `summingInt()`, or `toSet()`.

`groupingBy` is generally appropriate for sequential streams. For parallel streams, `groupingByConcurrent` can be considered when concurrent accumulation and a concurrent map are appropriate; the choice depends on the workload and required map semantics.

Key points

Interview questions

How can you group elements and transform the values at the same time?

Supply a downstream collector such as `mapping`. For example, `groupingBy(Employee::department, mapping(Employee::name, toSet()))` creates a map from departments to sets of employee names. The outer collector decides the groups, while the downstream collector controls what is retained inside each group. This separates classification from aggregation.

What does `Collectors.groupingBy` do?

It groups stream elements according to a classifier function. The classifier produces the key for each element, and elements with the same key are accumulated together. With the basic overload, the result is a `Map<K, List<T>>`. It is commonly used for operations such as grouping employees by department.

How would you count elements in each group?

Use the downstream `Collectors.counting()` collector. For example, `stream.collect(Collectors.groupingBy(Item::category, Collectors.counting()))` produces a `Map<Category, Long>`. Each category is still identified by the classifier, but the values are counts instead of lists.

What is the difference between `groupingBy` and `toMap`?

`groupingBy` is intended for many-to-one classification, so multiple stream elements can naturally belong to the same key. Its basic form collects those elements into a list. `toMap` instead constructs key-value mappings and requires a merge strategy when duplicate keys occur. Therefore, `groupingBy` is usually clearer when the requirement is explicitly to form groups.

When would you use `groupingByConcurrent` instead of `groupingBy`?

`groupingByConcurrent` is intended for concurrent grouping and produces a concurrent map. It can be useful with parallel processing when the workload benefits from concurrent accumulation and the resulting map's concurrency characteristics are appropriate. It should not be selected merely because a stream is parallel; the actual aggregation cost, contention, and required semantics matter. If concurrency is unnecessary, ordinary `groupingBy` is usually the simpler abstraction.