Получить доступ

Stream API: устройство и ленивый конвейер

Что такое стрим, источники, промежуточные против терминальных, ленивость и short-circuiting, stateful-операции, параллельность и примитивные стримы.

Что такое Stream

Stream — это не структура данных, а конвейер обработки последовательности элементов. Он не хранит элементы (тянет их из источника), не изменяет источник и одноразовый: после терминальной операции стрим использован, повторно по нему не пройти.

Идея — описать ЧТО сделать с данными декларативно (filter→map→collect), а не КАК итерировать вручную.

Источники

Стрим получают из коллекции (collection.stream(), parallelStream()), массива (Arrays.stream), явных значений (Stream.of), диапазона (IntStream.range), строк файла (Files.lines) и генераторов (Stream.iterate/generate — потенциально бесконечные).

Бесконечные источники безопасны только с обрывающими операциями (limit, findFirst) — иначе обработка не завершится.

Промежуточные против терминальных

Операции делятся на промежуточные (intermediate) — возвращают новый Stream и ленивы (map, filter, sorted, distinct, limit, peek) — и терминальные (terminal) — запускают обработку и дают результат/побочный эффект (collect, forEach, reduce, count, findFirst, anyMatch).

Без терминальной операции конвейер не выполнится вообще: цепочка map/filter без collect/forEach — это no-op. Ровно одна терминальная операция завершает стрим.

List<String> names = users.stream()
    .filter(User::isActive)  // ленивая (intermediate)
    .map(User::name)          // ленивая (intermediate)
    .toList();                // терминальная — здесь всё и считается

Ленивость и short-circuiting

Конвейер обрабатывает элементы по одному сквозь все операции (а не «весь filter, потом весь map»). Эта ленивость даёт два эффекта: операции сливаются (fusion) в один проход, и обрывающие операции (findFirst, anyMatch, limit) останавливают обработку, как только результат ясен — остальные элементы не трогаются.

Поэтому users.stream().filter(...).findFirst() не фильтрует весь список, а останавливается на первом подходящем. peek помогает увидеть реальный порядок обработки при отладке.

Ленивый конвейер Stream API: источник → промежуточные операции filter и map → терминальная операция; элементы проходят по одному.
Stream — ленивый конвейер: элемент проходит через filter и map к терминальной операции по одному, а не вся коллекция разом.

Stateless, stateful и параллельность

Операции бывают stateless (map, filter — элемент обрабатывается независимо) и stateful (sorted, distinct, limit — требуют состояния/буфера или знания обо всём входе, поэтому могут «материализовать» поток). Стрим обычно сохраняет encounter order источника.

parallelStream() распараллеливает обработку на общем ForkJoinPool. Это выгодно лишь при большом объёме, дорогой на элемент работе и хорошо делимом источнике (ArrayList — да, LinkedList — плохо). Ловушки: побочные эффекты в лямбдах (гонки), затраты на разбиение/слияние, общий commonPool, неупорядоченность — поэтому параллелизм включают по замеру, а не по умолчанию.

Примитивные стримы

Stream<Integer> боксит каждый элемент. Для чисел есть IntStream/LongStream/DoubleStream без боксинга — с удобными sum(), average(), summaryStatistics().

Переходы между ними: mapToInt/mapToObj/boxed. В числовой обработке это и быстрее, и выразительнее (range, rangeClosed).

int total = orders.stream().mapToInt(Order::amount).sum(); // без боксинга

Как отвечать на собеседовании

Скажите, что Stream — конвейер, не хранилище, одноразовый и не мутирует источник; разделите intermediate (ленивые) и terminal (запускает) и подчеркните, что без терминальной ничего не считается. Объясните ленивость + short-circuiting (один проход, findFirst/limit обрывают), stateful-операции и осознанный выбор parallel (объём/делимость/без side effects). Упомяните примитивные стримы против боксинга.

Вопросы по теме

Читайте также

Закрытая бета

Знать тему — половина дела

На живом собеседовании sotto слышит вопрос интервьюера и показывает короткий ответ и план. Оставьте контакт — пришлём доступ и промокод.

  • Промокод на 2 часа — сверх пробных 30 минут
  • Программа для Windows — скачать сразу, войти — через Яндекс ID или VK ID
  • Прямая связь с разработчиком: что улучшить — сделаем