Найти в Дзене
Записки о Java

Разбор 10 задач на Java Stream API

Условие:
У вас есть список Product, где каждый товар содержит name, category и price. Необходимо получить Map<String, List<Product>>, где для каждой категории содержится не более 3 самых дорогих товаров, отсортированных по убыванию цены. Пояснение: 💡 Для Java 11:
.collect(Collectors.toList()) вместо .toList(). Условие:
Получите уникальный набор положительных чисел из вложенного списка. Пояснение: Условие:
Дана строка. Нужно найти букву, встречающуюся чаще всего. Если таких несколько — вернуть любую. Пояснение: Условие:
Например: все числа ≥ 0, и хотя бы одно > 100. Оптимизация (один проход): ⚠️ Но: teeing появился в Java 12, поэтому в Java 11 используем два прохода (либо кастомный коллектор — см. задачу 10). Условие:
List<User>, где User имеет name и age. Нужно получить Map<Integer, String> вида {25="Alice,Bob", 30="Charlie"}. Пояснение: Условие:
По списку строк построить Map<Character, Double>: ключ — первая буква, значение — средняя длина строк, начинающихся с неё. Пояснение:
Оглавление
Рисунок: задачи на stream API для JAVA
Рисунок: задачи на stream API для JAVA

Задача 1: Найти топ‑3 самых дорогих товаров в каждой категории

Условие:
У вас есть список Product, где каждый товар содержит name, category и price. Необходимо получить Map<String, List<Product>>, где для каждой категории содержится не более 3 самых дорогих товаров, отсортированных по убыванию цены.

Решение:

Рисунок: решение 1 задачи на stream API
Рисунок: решение 1 задачи на stream API

Пояснение:

  • groupingBy(category) — разбиваем по категориям.
  • collectingAndThen позволяет выполнить пост-обработку: сортировку и лимит.
  • toList() (Java 16+, но в Java 11 замените на Collectors.toList()).
💡 Для Java 11:
.collect(Collectors.toList()) вместо .toList().

Задача 2: Преобразовать List<List<Integer>> в плоский Set<Integer>, исключив дубликаты и отрицательные числа

Условие:
Получите уникальный набор положительных чисел из вложенного списка.

Решение:

Рисунок: решение 2 задачи на stream API
Рисунок: решение 2 задачи на stream API

Пояснение:

  • flatMap «выпрямляет» вложенную структуру.
  • filter отсекает неположительные.
  • toSet() гарантирует уникальность.

Задача 3: Найти самую частую букву в строке (игнорируя регистр и не-буквы)

Условие:
Дана строка. Нужно найти букву, встречающуюся чаще всего. Если таких несколько — вернуть любую.

Решение:

Рисунок: решение задачи на stream API
Рисунок: решение задачи на stream API

Пояснение:

  • Преобразуем строку в IntStream, фильтруем буквы.
  • Группируем по символу с подсчётом.
  • Находим максимум по значению.

Задача 4: Проверить, что все элементы списка удовлетворяют условию и при этом хотя бы один элемент удовлетворяет более строгому условию

Условие:
Например: все числа ≥ 0, и хотя бы одно > 100.

Решение:

Рисунок: решение задачи 4
Рисунок: решение задачи 4

Оптимизация (один проход):

Рисунок: оптимизация задачи 4
Рисунок: оптимизация задачи 4

⚠️ Но: teeing появился в Java 12, поэтому в Java 11 используем два прохода (либо кастомный коллектор — см. задачу 10).

Задача 5: Сгруппировать пользователей по возрасту и для каждой группы вывести имена через запятую

Условие:
List<User>, где User имеет name и age. Нужно получить Map<Integer, String> вида {25="Alice,Bob", 30="Charlie"}.

Решение:

Рисунок: решение задачи 5
Рисунок: решение задачи 5

Пояснение:

  • mapping преобразует объекты в имена.
  • joining объединяет их.

Задача 6: Получить среднюю длину строки для каждого уникального первого символа

Условие:
По списку строк построить Map<Character, Double>: ключ — первая буква, значение — средняя длина строк, начинающихся с неё.

Решение:

Рисунок: решение задачи 6
Рисунок: решение задачи 6

Пояснение:

  • averagingInt напрямую вычисляет среднее.
  • Фильтруем пустые строки, чтобы избежать исключения.

Задача 7: Разбить список на страницы заданного размера

Условие:
Реализовать List<List<T>> paginate(List<T> list, int pageSize) с использованием Stream API без циклов.

Решение:

Рисунок: решение задачи 7
Рисунок: решение задачи 7

Пояснение:

  • IntStream.iterate эмулирует цикл по индексам.
  • subList создаёт «страницы» без копирования (view).

Задача 8: Найти все дубликаты в списке с подсчётом их количества

Условие:
Вернуть Map<T, Long> только для элементов, встречающихся более одного раза.

Решение:

Рисунок: решение задачи 8
Рисунок: решение задачи 8

Пояснение:

  • Сначала группируем с подсчётом.
  • Затем фильтруем по количеству.

Задача 9: Преобразовать Map<String, List<Integer>> в обратный индекс: Map<Integer, Set<String>>

Условие:
Для каждого числа указать, в каких ключах оно встречается.

Решение:

Рисунок: решение задачи 9
Рисунок: решение задачи 9

Пояснение:

  • Преобразуем каждую пару (ключ, список) в поток (число, ключ).
  • Группируем по числу, собирая ключи в Set.

Задача 10: Написать кастомный коллектор, который считает количество чётных и нечётных чисел

Условие:
Создать Collector<Integer, ?, Map<String, Long>>, возвращающий {even=10, odd=15}.

Решение:

Рисунок: решение задачи 10
Рисунок: решение задачи 10

Или полностью кастомный коллектор (для демонстрации):

Рисунок: второе решение задачи 10
Рисунок: второе решение задачи 10

Пояснение:

  • Показывает понимание Collector.of.
  • Подходит для параллельных потоков (combiner корректен).