Collectors.groupingBy — это метод, который используется для группировки элементов потока (Stream) на основе заданного критерия. Он позволяет создать Map, где ключом будет выступать значение, возвращаемое функцией группировки, а значением — коллекция элементов, которые соответствуют этому ключу. Рассмотрим принцип работы Collectors.groupingBy и его различные варианты.
Общий алгоритм работы Collectors.groupingBy
- Создание потока данных:
Collectors.groupingByприменяется к потоку (Stream) элементов. - Определение функции группировки: Сначала вы указываете функцию, которая будет определять ключи для группировки элементов.
- Указание коллектора для значений (необязательно): Дополнительно можно указать, как собирать значения для каждого ключа — это может быть, например,
Collectors.toList()для списка илиCollectors.counting()для подсчета количества элементов. - Выбор типа карты (необязательно): По умолчанию используется
HashMap, но можно указать другой тип карты, например,TreeMap, если нужен порядок по ключам.
Варианты использования Collectors.groupingBy
- Базовая группировка (по умолчанию —
HashMap) В этом случаеgroupingByсоздает карту, где каждый ключ — это результат функции группировки, а значение — список элементов, соответствующих этому ключу.Map<String, List<Programmer>> map = programmers.stream() .collect(Collectors.groupingBy(Programmer::getName));Алгоритм:- Для каждого программиста
getNameвозвращает имя, которое используется как ключ. - Все программисты с одинаковым именем собираются в один список, который и становится значением для этого ключа.
- Для каждого программиста
- Группировка с модификацией значений (например,
Collectors.counting()) Можно подсчитать количество элементов в каждой группе. ВместоList<Programmer>в значении карты будетLong, представляющий количество элементов.Map<String, Long> map = programmers.stream() .collect(Collectors.groupingBy(Programmer::getName, Collectors.counting()));Алгоритм:getNameпо-прежнему возвращает ключ (имя программиста).- Вместо списка значений используется
Collectors.counting(), который подсчитывает количество элементов в каждой группе и возвращаетLong.
- Группировка с указанием типа карты По умолчанию используется
HashMap, но можно указать другой тип карты, например,TreeMapилиLinkedHashMap.Map<String, List<Programmer>> map = programmers.stream() .collect(Collectors.groupingBy(Programmer::getName, TreeMap::new, Collectors.toList()));Алгоритм:getNameвозвращает имя, используемое в качестве ключа.TreeMap::newуказывает, что карта должна бытьTreeMap, что сортирует её по ключам.Collectors.toList()собирает элементы каждой группы в список.
- Группировка по вычисленному ключу Допустим, вы хотите сгруппировать программистов по количеству технологий, которыми они владеют. В этом случае ключом будет количество технологий, а значением — список программистов с этим количеством.
Map<Integer, List<Programmer>> map = programmers.stream() .collect(Collectors.groupingBy(programmer -> programmer.getTechnologies().size()));Алгоритм:- Ключ — это результат
programmer.getTechnologies().size()(количество технологий у программиста). - Программисты с одинаковым количеством технологий собираются в списки.
- Ключ — это результат
- Многоуровневая группировка Можно вложить несколько
groupingByдля создания вложенной карты. Например, сгруппируем программистов по количеству технологий, а затем внутри каждой группы — по имени.Map<Integer, Map<String, List<Programmer>>> map = programmers.stream() .collect(Collectors.groupingBy( programmer -> programmer.getTechnologies().size(), // Внешний ключ — количество технологий Collectors.groupingBy(Programmer::getName) // Внутренний ключ — имя ));Алгоритм:- Сначала группируем программистов по количеству технологий.
- Внутри каждой такой группы применяем еще одну группировку по имени.
Резюме
Collectors.groupingByпозволяет легко группировать элементы потока по определенному критерию.- Ключи карты определяются функцией группировки.
- Значения можно модифицировать, применяя другие коллекторы, например,
Collectors.counting(),Collectors.toSet()и т.д. - При необходимости можно указать тип карты или использовать многоуровневую группировку.
Надеюсь, этот алгоритм поможет вам эффективно использовать Collectors.groupingBy для разных задач!
