Методы Stream<T>

Stream — ленивый одноразовый pipeline, а не коллекция. Источник обходится после terminal operation.

Промежуточные операции

ОперацияРоль
filterоставить элементы по predicate
map / flatMapпреобразовать / развернуть вложенные источники
peekнаблюдение, главным образом диагностика
distinctубрать дубликаты по equals/hashCode
sortedотсортировать
limit / skipограничить/пропустить
takeWhile / dropWhileвзять/отбросить начальный префикс

distinct и sorted — stateful: им нужно помнить элементы. На ordered stream dropWhile отбрасывает только самый длинный начальный префикс, а не все совпавшие элементы.

Терминальные операции

ОперацияРезультат
toList, collectматериализовать результат
reduceсвернуть значения
count, min, maxагрегировать
findFirst, findAnyнайти элемент
anyMatch, allMatch, noneMatchshort-circuit проверка
forEach, forEachOrderedвыполнить действие
Map<Status, Long> countByStatus = tasks.stream()
        .filter(Task::isVisible)
        .collect(Collectors.groupingBy(
                Task::status,
                () -> new EnumMap<>(Status.class),
                Collectors.counting()
        ));

Stream.toList() возвращает unmodifiable list. Collectors.toList() не обещает конкретный тип и mutability — если нужна изменяемость, указать supplier явно.

List<String> mutable = stream.collect(
        Collectors.toCollection(ArrayList::new)
);

Нюансы

  • allMatch и noneMatch на пустом stream возвращают true, anyMatch — false;
  • findFirst сохраняет encounter order, findAny может дать больше свободы parallel pipeline;
  • Stream.concat(a, b) — static-операция создания нового stream; его закрытие закрывает оба исходных;
  • mutable side effects в map/filter ломают рассуждение и parallel execution;
  • бесконечный stream требует short-circuit/limit для завершения.

На собеседовании

Чем intermediate отличается от terminal? Первая строит ленивый pipeline, вторая запускает обход.

Почему peek может не выполниться для каждого элемента? Pipeline ленив и может short-circuit/оптимизировать обход.

Что вернёт allMatch для пустого stream? true.

Можно ли повторно вызвать terminal operation? Нет, stream уже consumed.

Гарантирует ли toList() изменяемый список? Нет, Stream.toList() возвращает unmodifiable list.

Связано: Промежуточные операции, Терминальные операции, BaseStream.