0Pricing
Java Academy · Урок

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 — локальная установка не требуется.

Все уроки этого курса

  1. groupingBy: классификация элементов
  2. partitioningBy и подсчёт
  3. toMap, joining и summarizing
  4. Создание собственного сборщика
← Назад к Java Academy