How to group a list by a field
Short answer
Collectors.groupingBy() collects elements into a Map where the key is the field value and the value is a list of elements:
record Employee(String name, String department) {}
void main() {
var staff = List.of(
new Employee("Anna", "IT"),
new Employee("Oleg", "Sales"),
new Employee("Boris", "IT"));
Map<String, List<Employee>> byDepartment = staff.stream()
.collect(Collectors.groupingBy(Employee::department));
IO.println(byDepartment.get("IT"));
}Output:
[Employee[name=Anna, department=IT], Employee[name=Boris, department=IT]]Sorted keys
By default the result is a HashMap with no order. Pass TreeMap::new to get keys in alphabetical order:
void main() {
var words = List.of("apple", "bob", "avocado", "cat", "banana");
var byLetter = words.stream()
.collect(Collectors.groupingBy(w -> w.charAt(0), TreeMap::new, Collectors.toList()));
IO.println(byLetter);
}Output:
{a=[apple, avocado], b=[bob, banana], c=[cat]}Only the field you need
mapping() transforms the group's elements before collecting:
record Employee(String name, String department) {}
void main() {
var staff = List.of(new Employee("Anna", "IT"), new Employee("Oleg", "Sales"), new Employee("Boris", "IT"));
var names = staff.stream().collect(Collectors.groupingBy(Employee::department, TreeMap::new,
Collectors.mapping(Employee::name, Collectors.toList())));
IO.println(names);
}Output:
{IT=[Anna, Boris], Sales=[Oleg]}Counting per group
counting() gives the number of elements instead of a list:
void main() {
var words = List.of("one", "two", "three", "four", "five", "six");
var byLength = words.stream()
.collect(Collectors.groupingBy(String::length, TreeMap::new, Collectors.counting()));
IO.println(byLength);
}Output:
{3=3, 4=2, 5=1}Common mistake
Grouping by a field that can be null. groupingBy() does not accept null as a key:
record Employee(String name, String department) {}
void main() {
var staff = List.of(new Employee("Anna", "IT"), new Employee("Ivan", null));
staff.stream().collect(Collectors.groupingBy(Employee::department));
}Output:
Exception in thread "main" java.lang.NullPointerException: element cannot be mapped to a null keySubstitute a default value: e -> Objects.requireNonNullElse(e.department(), "none").
Functions used in this recipe
- Collectors.groupingBy() — A collector that sorts elements into groups that share a key.
- Collectors.counting() — A counting collector, usually used inside `groupingBy()`.
- Collectors.partitioningBy() — Splits stream elements into two groups by a condition.