← Назад к вопросам

Что такое группировка?

1.7 Middle🔥 171 комментариев
#Базы данных и SQL

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Группировка (Grouping) в Java Streams

Группировка — это операция потокового API Java, которая позволяет разделить элементы коллекции на группы по одному или нескольким критериям и собрать результат в виде Map'а, где ключи — это критерии группировки, а значения — коллекции элементов.

Синтаксис

import java.util.stream.Collectors;

Map<Key, List<Element>> grouped = elements.stream()
    .collect(Collectors.groupingBy(element -> groupingKey));

Простой пример

public class Person {
    private String name;
    private String city;
    private int age;
    
    // конструктор, геттеры...
}

public static void main(String[] args) {
    List<Person> people = Arrays.asList(
        new Person("Иван", "Москва", 30),
        new Person("Мария", "СПб", 25),
        new Person("Петр", "Москва", 35),
        new Person("Анна", "СПб", 28)
    );
    
    // Группировать людей по городу
    Map<String, List<Person>> peopleByCity = people.stream()
        .collect(Collectors.groupingBy(Person::getCity));
    
    /* Результат:
    {
        "Москва": [Иван, Петр],
        "СПб": [Мария, Анна]
    }
    */
    
    peopleByCity.forEach((city, persons) -> {
        System.out.println(city + ": " + persons);
    });
}

Вложенная группировка

Можно группировать по нескольким критериям одновременно:

Map<String, Map<Integer, List<Person>>> groupedByCityAndAge = people.stream()
    .collect(Collectors.groupingBy(
        Person::getCity,
        Collectors.groupingBy(Person::getAge)
    ));

/* Результат:
{
    "Москва": {
        30: [Иван],
        35: [Петр]
    },
    "СПб": {
        25: [Мария],
        28: [Анна]
    }
}
*/

Группировка с подсчетом

Map<String, Long> countByCity = people.stream()
    .collect(Collectors.groupingBy(
        Person::getCity,
        Collectors.counting()
    ));

/* Результат:
{
    "Москва": 2,
    "СПб": 2
}
*/

Группировка с суммированием

Map<String, Integer> totalAgeByCity = people.stream()
    .collect(Collectors.groupingBy(
        Person::getCity,
        Collectors.summingInt(Person::getAge)
    ));

/* Результат:
{
    "Москва": 65,
    "СПб": 53
}
*/

Группировка с условием

Фильтрация перед группировкой:

Map<String, List<Person>> adultsOver25 = people.stream()
    .filter(p -> p.getAge() > 25)
    .collect(Collectors.groupingBy(Person::getCity));

Группировка с преобразованием значений

Map<String, List<String>> namesByCity = people.stream()
    .collect(Collectors.groupingBy(
        Person::getCity,
        Collectors.mapping(Person::getName, Collectors.toList())
    ));

/* Результат:
{
    "Москва": ["Иван", "Петр"],
    "СПб": ["Мария", "Анна"]
}
*/

Практический пример: Анализ заказов

public class Order {
    private int id;
    private String customer;
    private String status;  // "delivered", "pending", "cancelled"
    private double amount;
    
    // конструктор, геттеры...
}

List<Order> orders = Arrays.asList(
    new Order(1, "Иван", "delivered", 1000),
    new Order(2, "Мария", "pending", 2000),
    new Order(3, "Иван", "delivered", 1500),
    new Order(4, "Петр", "cancelled", 500)
);

// Сумма заказов по статусу
Map<String, Double> amountByStatus = orders.stream()
    .collect(Collectors.groupingBy(
        Order::getStatus,
        Collectors.summingDouble(Order::getAmount)
    ));

// Количество заказов по клиенту
Map<String, Long> countByCustomer = orders.stream()
    .collect(Collectors.groupingBy(
        Order::getCustomer,
        Collectors.counting()
    ));

// Группировка по клиенту и статусу
Map<String, Map<String, List<Order>>> ordersByCustomerAndStatus = orders.stream()
    .collect(Collectors.groupingBy(
        Order::getCustomer,
        Collectors.groupingBy(Order::getStatus)
    ));

Группировка в TreeMap (отсортированная)

Map<String, List<Person>> sortedByCity = people.stream()
    .collect(Collectors.groupingBy(
        Person::getCity,
        TreeMap::new,  // используем TreeMap вместо HashMap
        Collectors.toList()
    ));

Группировка с partition (бинарное разделение)

Map<Boolean, List<Person>> adultsSplit = people.stream()
    .collect(Collectors.partitioningBy(p -> p.getAge() >= 18));

/* Результат:
{
    true: [Иван, Мария, Петр, Анна],  // все взрослые
    false: []
}
*/

Сложный пример: Аналитика

var stats = people.stream()
    .collect(Collectors.groupingBy(
        Person::getCity,
        Collectors.collectingAndThen(
            Collectors.toList(),
            list -> new CityStats(
                list.size(),
                list.stream().mapToInt(Person::getAge).average().orElse(0)
            )
        )
    ));

record CityStats(int count, double avgAge) {}

Производительность

  • groupingBy() создает HashMap по умолчанию — O(n) по времени
  • Для больших данных можно использовать параллельные потоки
Map<String, List<Person>> grouped = people.parallelStream()
    .collect(Collectors.groupingByConcurrent(Person::getCity));

Итого

Группировка (groupingBy) — мощный инструмент Stream API для разделения данных по критериям. Позволяет одновременно фильтровать, преобразовывать и агрегировать данные. Используется для аналитики, статистики, и логического структурирования коллекций.

Что такое группировка? | PrepBro