groupingBy: классификация элементов
Группируйте элементы потока в карты по функции-классификатору и объединяйте её с последующими сборщиками.
«groupingBy: классификация элементов» — бесплатный урок Java Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Java Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Java Academy содержит 4 уроков всего.
Что такое groupingBy?
Collectors.groupingBy() разделяет элементы потока по группам и возвращает Map, где каждому ключу соответствует список подходящих элементов. Это потоковый аналог SQL-конструкции GROUP BY.
import java.util.*;
import java.util.stream.*;
record Person(String name, String city) {}
List<Person> people = List.of(
new Person("Alice","NYC"), new Person("Bob","LA"),
new Person("Carol","NYC"), new Person("Dave","LA"),
new Person("Eve","Chicago")
);
Map<String, List<Person>> byCity =
people.stream().collect(Collectors.groupingBy(Person::city));
byCity.forEach((city, ps) -> System.out.println(city + ": " + ps.size()));Последующий сборщик: counting
Объединяйте groupingBy с последующим сборщиком, чтобы выполнять агрегацию вместо формирования списков:
Map<String, Long> countByCity =
people.stream().collect(
Collectors.groupingBy(Person::city, Collectors.counting())
);
countByCity.forEach((city, n) -> System.out.println(city + " -> " + n));
// NYC -> 2, LA -> 2, Chicago -> 1Последующий сборщик: mapping
Используйте Collectors.mapping() как последующий сборщик, чтобы преобразовать элементы перед их сбором:
Map<String, List<String>> namesByCity =
people.stream().collect(
Collectors.groupingBy(
Person::city,
Collectors.mapping(Person::name, Collectors.toList())
)
);
namesByCity.forEach((city, names) -> System.out.println(city + ": " + names));Последующий сборщик: joining
Объединяйте имена каждой группы в одну строку, разделённую запятыми:
Map<String, String> joinedByCity =
people.stream().collect(
Collectors.groupingBy(
Person::city,
Collectors.mapping(Person::name, Collectors.joining(", "))
)
);
joinedByCity.forEach((city, s) -> System.out.println(city + ": " + s));
// NYC: Alice, CarolМногоуровневый groupingBy
Вкладывайте вызовы groupingBy друг в друга для классификации по нескольким измерениям:
record Employee(String name, String dept, String level) {}
List<Employee> emps = List.of(
new Employee("A","Eng","Junior"), new Employee("B","Eng","Senior"),
new Employee("C","HR","Junior"), new Employee("D","HR","Senior")
);
Map<String, Map<String, List<Employee>>> grouped =
emps.stream().collect(
Collectors.groupingBy(Employee::dept,
Collectors.groupingBy(Employee::level))
);
grouped.forEach((dept, levels) -> levels.forEach((level, list) ->
System.out.println(dept+"/"+level+": "+list.size())));Управление типом Map
По умолчанию groupingBy создаёт HashMap. Используйте перегрузку с тремя аргументами, чтобы указать другой тип карты, например TreeMap для сортировки ключей:
Map<String, Long> sorted =
people.stream().collect(
Collectors.groupingBy(Person::city, TreeMap::new, Collectors.counting())
);
System.out.println(sorted); // keys in alphabetical orderПоследующий сборщик: averagingInt
Вычисляйте средние значения для каждой группы:
record Product(String category, int price) {}
List<Product> products = List.of(
new Product("A",10), new Product("A",20),
new Product("B",15), new Product("B",25)
);
Map<String, Double> avgPrice =
products.stream().collect(
Collectors.groupingBy(Product::category,
Collectors.averagingInt(Product::price))
);
avgPrice.forEach((cat, avg) -> System.out.printf("%s: %.1f%n", cat, avg));Последующий сборщик: toUnmodifiableList
Используйте Collectors.toUnmodifiableList() как последующий сборщик, чтобы создавать неизменяемые группы:
Map<String, List<String>> immutable =
people.stream().collect(
Collectors.groupingBy(Person::city,
Collectors.mapping(Person::name, Collectors.toUnmodifiableList()))
);
// Attempting to modify throws UnsupportedOperationExceptionПрактический пример: сводка по заказам
Сгруппируйте заказы по статусу и просуммируйте суммы в каждой группе:
record Order(String status, double amount) {}
List<Order> orders = List.of(
new Order("PAID", 50.0), new Order("PENDING", 30.0),
new Order("PAID", 75.0), new Order("CANCELLED", 20.0)
);
Map<String, Double> totalByStatus =
orders.stream().collect(
Collectors.groupingBy(Order::status,
Collectors.summingDouble(Order::amount))
);
totalByStatus.forEach((s, t) -> System.out.printf("%s: $%.2f%n", s, t));Особенности производительности
groupingBy загружает все элементы в память. Для очень больших наборов данных рассмотрите обработку частями или использование GROUP BY на уровне базы данных. Последующий сборщик выполняется для каждого элемента — сохраняйте его лёгким, чтобы избежать поведения O(n²).
Объединение с filter и sorted
Объединяйте операции над потоком перед сбором, чтобы предварительно отфильтровать или отсортировать данные:
Map<String, Long> activeCities =
people.stream()
.filter(p -> !p.city().equals("Chicago"))
.collect(Collectors.groupingBy(Person::city, Collectors.counting()));
System.out.println(activeCities); // {NYC=2, LA=2}Быстрая проверка
Какой тип возвращает Collectors.groupingBy(Person::city) при использовании вместе с collect()?
Повторение: groupingBy
Основные выводы:
- groupingBy(классификатор) → карта<K, список<T>>
- Объединяйте с последующими сборщиками: counting, mapping, joining, summarizing
- Вкладывайте groupingBy друг в друга для классификации по нескольким измерениям
- Используйте перегрузку с тремя аргументами, чтобы управлять типом результирующей карты, например TreeMap
Часто задаваемые вопросы
Урок «groupingBy: классификация элементов» бесплатный?
Да — полный текст урока «groupingBy: классификация элементов» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Java Academy, подпишись на CoddyKit PRO. Курс Java Academy содержит 4 уроков всего.
Чему я научусь в уроке «groupingBy: классификация элементов»?
Группируйте элементы потока в карты по функции-классификатору и объединяйте её с последующими сборщиками. Ты практикуешь Java Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Java Academy?
Предыдущий опыт не требуется. Java Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.
Сколько времени занимает урок «groupingBy: классификация элементов»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Java Academy?
Да. Каждый урок Java Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- groupingBy: классификация элементов
- partitioningBy и подсчёт
- toMap, joining и summarizing
- Создание собственного сборщика