Что такое группировка элементов в Java Stream API?

Ответ

Группировка в Java Stream API — это операция терминального сбора (collect), которая распределяет элементы потока (Stream) по различным группам на основе заданной функции-классификатора. Результатом является Map<K, List<V>>, где ключ (K) — результат функции классификатора, а значение — список элементов, попавших в эту группу.

Базовый пример: Группировка строк по их длине.

import java.util.*;
import java.util.stream.*;

List<String> words = Arrays.asList("apple", "banana", "fig", "cherry", "date");

Map<Integer, List<String>> wordsByLength = words.stream()
        .collect(Collectors.groupingBy(String::length));

// Результат в wordsByLength:
// {3=[fig], 4=[date], 5=[apple], 6=[banana, cherry]}

Расширенные возможности с Collectors.groupingBy:

  1. Группировка с последующим сбором (downstream collector): Можно не просто собирать элементы в List, а сразу выполнить над ними другую агрегацию.

    // Подсчет количества слов каждой длины
    Map<Integer, Long> countByLength = words.stream()
            .collect(Collectors.groupingBy(String::length, Collectors.counting()));
    // Результат: {3=1, 4=1, 5=1, 6=2}
  2. Изменение типа возвращаемой Map:

    // Группировка в TreeMap (отсортированная по ключу)
    Map<Integer, List<String>> sortedMap = words.stream()
            .collect(Collectors.groupingBy(String::length, TreeMap::new, Collectors.toList()));
  3. Многоуровневая (каскадная) группировка:

    // Сначала по длине, затем по первой букве
    Map<Integer, Map<Character, List<String>>> multiLevel = words.stream()
        .collect(Collectors.groupingBy(String::length,
                 Collectors.groupingBy(s -> s.charAt(0))));

Ключевой метод: java.util.stream.Collectors.groupingBy().

Ответ 18+ 🔞

А, группировка в стримах, ну это ж классика, блядь! Сидишь такой, пишешь код, а тут надо разложить всё по полочкам, как в сортире — бумажки отдельно, журналы отдельно, а хуй с горы — в ведро. Так вот, groupingBy — это твоя волшебная палочка, которая из потока данных делает аккуратную мапу, где всё разложено по кучкам. Как дворник-перфекционист, только в коде.

Смотри, в чём суть, ёпта. Берёшь ты поток, допустим, слов. И говоришь: «Солнышко, разложи-ка мне их по длине, а?». И он, такой умный, делает Map<Integer, List<String>>. Ключ — длина, значение — список всех слов такой длины. Проще пареной репы, аж в рот меня чих-пых!

import java.util.*;
import java.util.stream.*;

List<String> words = Arrays.asList("apple", "banana", "fig", "cherry", "date");

Map<Integer, List<String>> wordsByLength = words.stream()
        .collect(Collectors.groupingBy(String::length));

// Смотри, что в мапе вышло, блядь:
// {3=[fig], 4=[date], 5=[apple], 6=[banana, cherry]}

Ну и что, скажешь, это и обезьяна напишет? А вот хуй там! Сила-то groupingBy в том, что ты можешь не просто в лист складывать, а сразу в каждой кучке делать какую-нибудь дичь. Например, посчитать, сколько там слов в каждой кучке, чтобы не париться потом с размерами.

// Считаем, сколько слов каждой длины, одним махом, блядь!
Map<Integer, Long> countByLength = words.stream()
        .collect(Collectors.groupingBy(String::length, Collectors.counting()));
// Получаем: {3=1, 4=1, 5=1, 6=2} — два слова по шесть букв, всё чётко.

А ещё, представляешь, можно заказать не абы какую мапу, а конкретную. Скажешь: «Хочу TreeMap, чтобы ключи были по порядку, а то в HashMap они как попало лежат, волнение ебать!». И пожалуйста:

// Группируем сразу в TreeMap, чтоб ключи отсортированы были. Красота!
Map<Integer, List<String>> sortedMap = words.stream()
        .collect(Collectors.groupingBy(String::length, TreeMap::new, Collectors.toList()));

Но это ещё цветочки, блядь. А вот ягодки — это когда группировка на группировке сидит и группировкой погоняет. Каскадная, ёперный театр! Сначала по длине, а внутри каждой длины — ещё и по первой букве. Получается мапа в мапе, как матрёшка, только полезная.

// Сначала по длине, потом по первой букве. Овердохуища вложенности!
Map<Integer, Map<Character, List<String>>> multiLevel = words.stream()
        .collect(Collectors.groupingBy(String::length,
                 Collectors.groupingBy(s -> s.charAt(0))));

Вот и весь сказ, блядь. Collectors.groupingBy() — твой главный инструмент для наведения порядка в этом хаосе данных. Главное — не перегруппировать всё в одну кучу, а то получится пиздец, а не код.