Как сгруппировать список по полю
Короткий ответ
Collectors.groupingBy() собирает элементы в Map, где ключ — значение поля, а значение — список элементов:
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"));
}Результат:
[Employee[name=Anna, department=IT], Employee[name=Boris, department=IT]]Сортированные ключи
По умолчанию результат — HashMap без порядка. Передайте TreeMap::new, чтобы ключи шли по алфавиту:
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);
}Результат:
{a=[apple, avocado], b=[bob, banana], c=[cat]}Только нужное поле
mapping() превращает элементы группы перед сбором:
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);
}Результат:
{IT=[Anna, Boris], Sales=[Oleg]}Подсчёт в группах
counting() вместо списка даёт количество элементов:
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);
}Результат:
{3=3, 4=2, 5=1}Частая ошибка
Группировать по полю, которое может быть null. groupingBy() не принимает null в качестве ключа:
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));
}Результат:
Exception in thread "main" java.lang.NullPointerException: element cannot be mapped to a null keyПодставьте значение по умолчанию: e -> Objects.requireNonNullElse(e.department(), "none").
Функции из этого ответа
- Collectors.groupingBy() — Сборщик, который раскладывает элементы по группам с общим ключом.
- Collectors.counting() — Сборщик-счётчик; обычно используется внутри `groupingBy()`.
- Collectors.partitioningBy() — Делит элементы потока на две группы по условию.