


Collectors.groupingBy в Java собирает элементы потока в группы по ключу. Простой вариант возвращает Map<K, List<T>>. Если вместо списков нужны количество или сумма, передайте второй коллектор: например, counting() или summingInt(...).
В примере Mentoring.digital сгруппируем заказы по статусу. Это локальная обработка коллекции в памяти. Она не заменяет SQL GROUP BY, когда выгоднее агрегировать большой набор непосредственно в базе данных.
Код использует record и подходит для Java 17 и новее. Сохраните его в GroupOrders.java, затем выполните javac GroupOrders.java и java GroupOrders.
import java.util.*;
import java.util.stream.Collectors;
public class GroupOrders {
record Order(String status, int amount) {}
public static void main(String[] args) {
var orders = List.of(
new Order("paid", 1200),
new Order("waiting", 800),
new Order("paid", 500)
);
Map<String, List<Order>> groups = orders.stream()
.collect(Collectors.groupingBy(Order::status));
Map<String, Long> counts = orders.stream().collect(
Collectors.groupingBy(Order::status,
TreeMap::new, Collectors.counting())
);
Map<String, Integer> totals = orders.stream().collect(
Collectors.groupingBy(Order::status,
TreeMap::new, Collectors.summingInt(Order::amount))
);
System.out.println(groups.get("paid").size());
System.out.println(counts);
System.out.println(totals);
}
}
Первый вывод равен 2. Карта counts содержит {paid=2, waiting=1}, а totals содержит {paid=1700, waiting=800}. Порядок ключей в двух последних картах задаёт TreeMap. В первой карте конкретный тип реализации и порядок ключей не гарантированы.
Функция Order::status отвечает только за выбор группы. Дальнейшее действие внутри каждой группы задаёт downstream collector: собрать исходные объекты, посчитать их или сложить amount. Результат counting() имеет тип Long, поэтому карта с Integer в таком объявлении не подойдёт.
Если нужны только суммы, не обязательно сначала создавать карту списков, затем обходить каждый список. Передача агрегирующего коллектора сразу выражает нужный результат. Но она не делает вычисление автоматически быстрее во всех условиях, а сумма Integer может переполниться. Для реальных денежных расчётов нужно отдельно выбрать представление, масштаб и правила округления; учебные целые amount здесь только иллюстрируют группировку.
TreeMap сортирует ключи по их естественному порядку в этом примере. Если важен порядок первого появления ключа, можно передать LinkedHashMap::new и оставить последовательный поток. Порядок ключей карты и порядок элементов внутри каждой группы являются разными свойствами.
На пустом входе groupingBy вернёт пустую карту. Он не создаёт все возможные статусы со значением ноль. Для отчёта с фиксированным набором статусов заполните отсутствующие категории отдельно. Не делайте количество групп равным числу известных в системе статусов без такой обработки.
Обычный groupingBy не является concurrent collector. Добавлять parallelStream() ради предполагаемой скорости без измерения не стоит: объединение промежуточных карт тоже требует работы. Сначала убедитесь, что последовательный вариант выражает нужное правило.
На курсе Java с наставником этот пример можно развить в небольшой отчёт по данным приложения. Ментору по Java покажите тип исходных объектов, ожидаемую форму карты и требования к порядку, чтобы обсуждать не только синтаксис Stream API.