Collectors в Java
groupingBy
Collectors.groupingBy – один из самых мощных коллекторов в Java Stream API, позволяющий группировать элементы потока по заданному критерию. Возвращает Map<K, List<T>>, где ключ (K) – это значение классификатора, а значение – список элементов, соответствующих этому ключу.
1. Базовая форма: groupingBy(classifier)
Группирует элементы по ключу, возвращаемому classifier (функцией классификации).
Сигнатура:
Пример:
Как это работает внутри?
Классификатор (String::length) вычисляет ключ для каждого элемента.
Элементы с одинаковым ключом добавляются в List<T>.
По умолчанию используется HashMap, а значения хранятся в ArrayList.
2. groupingBy(classifier, downstream) – с дополнительным коллектором
Позволяет указать, как собирать элементы в группы (не только в List).
Сигнатура:
Примеры:
2.1. Группировка в Set вместо List
2.2. Подсчет количества элементов в каждой группе
2.3. Объединение элементов в строку
3. groupingBy(classifier, mapFactory, downstream) – с выбором реализации Map
Позволяет указать конкретную реализацию Map (например, TreeMap или LinkedHashMap).
Сигнатура:
Пример:
4. Многокритериальная группировка (вложенные groupingBy)
Можно группировать по нескольким уровням, создавая сложные структуры данных.
Пример:
5. Производительность и особенности
Порядок элементов в группах зависит от исходного потока (если он ordered, как в List, порядок сохраняется).
Хэш-коллизии обрабатываются через HashMap.
Нюансы при параллельных стримах:
Если используется ConcurrentHashMap, группировка становится потокобезопасной.
В некоторых случаях groupingByConcurrent работает быстрее.
#Java #Training #Medium #Collectors #groupingBy
groupingBy
Collectors.groupingBy – один из самых мощных коллекторов в Java Stream API, позволяющий группировать элементы потока по заданному критерию. Возвращает Map<K, List<T>>, где ключ (K) – это значение классификатора, а значение – список элементов, соответствующих этому ключу.
1. Базовая форма: groupingBy(classifier)
Группирует элементы по ключу, возвращаемому classifier (функцией классификации).
Сигнатура:
public static <T, K> Collector<T, ?, Map<K, List<T>>> groupingBy(
Function<? super T, ? extends K> classifier
)
Пример:
List<String> names = List.of("Alice", "Bob", "Charlie", "Anna", "Alex");
Map<Integer, List<String>> groupedByLength = names.stream()
.collect(Collectors.groupingBy(String::length));
// Результат:
// {3=[Bob], 4=[Anna, Alex], 5=[Alice], 7=[Charlie]}
Как это работает внутри?
Классификатор (String::length) вычисляет ключ для каждого элемента.
Элементы с одинаковым ключом добавляются в List<T>.
По умолчанию используется HashMap, а значения хранятся в ArrayList.
2. groupingBy(classifier, downstream) – с дополнительным коллектором
Позволяет указать, как собирать элементы в группы (не только в List).
Сигнатура:
public static <T, K, A, D> Collector<T, ?, Map<K, D>> groupingBy(
Function<? super T, ? extends K> classifier,
Collector<? super T, A, D> downstream
)
Примеры:
2.1. Группировка в Set вместо List
Map<Integer, Set<String>> groupedByLength = names.stream()
.collect(Collectors.groupingBy(
String::length,
Collectors.toSet()
));
2.2. Подсчет количества элементов в каждой группе
Map<Integer, Long> countByLength = names.stream()
.collect(Collectors.groupingBy(
String::length,
Collectors.counting()
));
// Результат: {3=1, 4=2, 5=1, 7=1}
2.3. Объединение элементов в строку
Map<Integer, String> joinedNames = names.stream()
.collect(Collectors.groupingBy(
String::length,
Collectors.joining(", ")
));
// Результат: {3="Bob", 4="Anna, Alex", 5="Alice", 7="Charlie"}
3. groupingBy(classifier, mapFactory, downstream) – с выбором реализации Map
Позволяет указать конкретную реализацию Map (например, TreeMap или LinkedHashMap).
Сигнатура:
public static <T, K, D, A, M extends Map<K, D>> Collector<T, ?, M> groupingBy(
Function<? super T, ? extends K> classifier,
Supplier<M> mapFactory,
Collector<? super T, A, D> downstream
)
Пример:
// Группировка с сохранением порядка (LinkedHashMap)
LinkedHashMap<Integer, List<String>> orderedGroups = names.stream()
.collect(Collectors.groupingBy(
String::length,
LinkedHashMap::new,
Collectors.toList()
));
// Группировка с сортировкой по ключу (TreeMap)
TreeMap<Integer, Set<String>> sortedGroups = names.stream()
.collect(Collectors.groupingBy(
String::length,
TreeMap::new,
Collectors.toSet()
));
4. Многокритериальная группировка (вложенные groupingBy)
Можно группировать по нескольким уровням, создавая сложные структуры данных.
Пример:
record Person(String name, String city, int age) {}
List<Person> people = List.of(
new Person("Alice", "NY", 25),
new Person("Bob", "LA", 30),
new Person("Charlie", "NY", 25),
new Person("David", "LA", 35)
);
// Группировка по городу, а затем по возрасту
Map<String, Map<Integer, List<Person>>> multiLevelGroup = people.stream()
.collect(Collectors.groupingBy(
Person::city,
Collectors.groupingBy(Person::age)
));
// Результат:
// {
// "NY": {25=[Alice, Charlie]},
// "LA": {30=[Bob], 35=[David]}
// }
5. Производительность и особенности
Порядок элементов в группах зависит от исходного потока (если он ordered, как в List, порядок сохраняется).
Хэш-коллизии обрабатываются через HashMap.
Нюансы при параллельных стримах:
Если используется ConcurrentHashMap, группировка становится потокобезопасной.
В некоторых случаях groupingByConcurrent работает быстрее.
#Java #Training #Medium #Collectors #groupingBy