Конвейер = источник + цепочка промежуточных операций + одна терминальная.
List<String> names = users.stream() // источник
.filter(User::isActive) // промежуточная
.map(User::name) // промежуточная
.limit(10) // промежуточная, короткое замыкание
.toList(); // терминальная
Ленивость. Пока не вызвана терминальная операция, filter и map не выполняются вовсе. Классическая проверка на собеседовании: стрим с peek(System.out::println) без терминальной операции ничего не печатает.
Поэлементная обработка. Первый элемент проходит filter → map → limit, затем второй. Поэтому limit(10) останавливает обработку после десяти подходящих элементов, даже если источник бесконечен.
Промежуточные операции:
- без состояния —
filter, map, flatMap, peek; - с состоянием —
sorted, distinct: должны накопить элементы; - короткого замыкания —
limit, takeWhile.
Терминальные: collect, toList, reduce, forEach, count, min/max, anyMatch/allMatch/noneMatch, findFirst/findAny.
Стрим не изменяет источник и не хранит данные; после терминальной операции он израсходован.