Потоки Java 30. Сбор 6. Collectors.groupingBy()

Терминальная операция либо возвращает одно значение (того же или другого типа, чем тип ввода), либо вообще ничего не возвращает (производит только побочные эффекты). Это не позволяет применить другую операцию после этого и закрывает поток.

В этом посте мы продолжим рассказывать о последней операции терминала, называемой collect():

R collect(Коллектор‹T,A,R› коллектор)

Это специализация операции reduce(). Он позволяет реализовать широкий спектр алгоритмов, используя готовые сборщики из класса java.util.stream.Collectors. Мы обсуждали, как создать собственный сборщик, в разделе Java streams 25. Сбор 1. Пользовательский сборщик. В этой статье мы будем использовать только сборщики, созданные классом Collectors.

Коллекторы.groupingBy()

Внимание! Коллектор Collectors.groupingBy() не является параллельным. Для параллельных потоков функция объединителя работает путем слияния ключей из одной карты в другую, что может быть дорогостоящей операцией. Если не требуется, чтобы результаты объединялись в Map в порядке их обнаружения, более высокая производительность параллельной обработки может быть достигнута с помощью Collectors.toConcurrentMap() или Collectors. коллектор .groupingByConcurrent(), о котором мы поговорим в следующих статьях.

Существует несколько перегруженных версий фабричных методов, создающих сборщик Collectors.groupingBy():

  • Collector‹T,?,Map‹K,List‹T››› groupingBy(Function‹T,K› classifier) — возвращает Collector, реализующий операцию «группировать по» для входных элементов типа T , группируя элементы в соответствии с функцией классификации и возвращая результаты в карту;
  • Collector‹T,?,Map‹K,D›› groupingBy(Function‹T,K› classifier, Collector‹T,A,D› нижестоящий) — возвращает Collector, реализующий каскадную «группировку по » операция над входными элементами типа T, группировка элементов в соответствии с функцией классификации, а затем выполнение операции редукции значений, связанных с данным ключом, с использованием указанного нисходящего коллектора;
  • Collector‹T,?,M› groupingBy(Function‹T,K› classifier, Supplier‹M› mapSupplier, Collector‹T,A,D› downstream) — возвращает Collector, реализующий каскадную «группу». by» над входными элементами типа T, группируя элементы в соответствии с функцией классификации, а затем выполняя операцию редукции над значениями, связанными с данным ключом, с использованием указанного нисходящего коллектора.

Функция классификатор извлекает из элемента значение, которое будет служить ключом в результирующей карте. Этот ключ сопоставляется со значением (по умолчанию List, но может быть любым другим объектом, созданным функцией нисходящей), которое содержит элементы потока, которые дали тот же ключ.

Нижестоящий – это сборщик, который создает объект (сопоставленный с ключом, созданным классификатором), в котором хранятся все элементы, давшие одно и то же значение ключа.

mapSupplier — это функция, которая при вызове создает новую пустую Map нужного типа, используемую нижестоящим для хранения элементов, которые дал одно и то же значение ключа.

Ниже приведены примеры использования Collectors.groupingBy():

Map<String, List<String>> map1 = 
                       Stream.of("cat", "fish", "cat", "dog")
        .collect(Collectors.groupingBy(Function.identity()));
System.out.print(map1); 
           //prints: {fish=[fish], cat=[cat, cat], dog=[dog]}
Map<Integer, List<String>> map2 = 
                     Stream.of("cat", "fish", "cat", "dog")
        .collect(Collectors.groupingBy(String::length));
System.out.print(map2); //prints: {3=[cat, cat, dog], 4=[fish]}
Map<Integer, Set<String>> map3 = 
                       Stream.of("cat", "fish", "cat", "dog")
      .collect(Collectors.groupingBy(String::length,      
                                        Collectors.toSet()));
System.out.print(map3);    //prints: {3=[cat, dog], 4=[fish]}
Map<String, Long> map4 = 
                     Stream.of("cat", "fish", "cat", "dog")
   .collect(Collectors.groupingBy(Function.identity(),                                                       
                                   Collectors.counting()));
System.out.print(map4);    //prints: {fish=1, cat=2, dog=1}
Map<Integer, Map<String, List<String>>> map5 = 
                     Stream.of("cat", "fish", "cat", "dog")
   .collect(Collectors.groupingBy(String::length, 
              Collectors.groupingBy(Function.identity())));
System.out.print(map5);    
 //prints: {3={cat=[cat, cat], dog=[dog]}, 4={fish=[fish]}}
Map<Integer, Map<String, Long>> map6 = 
                      Stream.of("cat", "fish", "cat", "dog")
   .collect(Collectors.groupingBy(String::length,                                     
              Collectors.groupingBy(Function.identity(),                                                 
                                   Collectors.counting())));
System.out.print(map6);    
                    //prints: {3={cat=2, dog=1}, 4={fish=1}}
ConcurrentMap<Integer, Map<String, Long>> map7 = 
                      Stream.of("cat", "fish", "cat", "dog")
   .collect(Collectors.groupingBy(String::length, 
                             ConcurrentHashMap::new,                                      
              Collectors.groupingBy(Function.identity(),                                                     
                                   Collectors.counting())));
System.out.print(map7);  
                    //prints: {3={cat=2, dog=1}, 4={fish=1}}

В следующем посте мы поговорим о создании объекта Map с помощью коллектора Collectors.toConcurrentMap().

Смотрите другие сообщения о потоках Java 8 и сообщения на другие темы.
Вы также можете использовать страницы навигации для блогов, связанных с потоками Java:
Заголовки блогов потоков Java 8
> — Создать поток
Потоковые операции
Потоковая операция collect()
Исходный код всех примеров кода находится здесь, на GitHub.