В чем разница между промежуточными и терминальными операциями в стримах Java
Что проверяют
Понимаете ли вы ленивость: без терминальной операции конвейер не выполняется вовсе.
Как отвечать
Промежуточные операции возвращают новый стрим и ничего не вычисляют: filter, map, flatMap, sorted, distinct, limit, peek. Они лишь описывают, что нужно сделать.
Терминальные запускают конвейер и завершают стрим: collect, forEach, reduce, count, min, anyMatch, findFirst, toList.
Stream<String> s = list.stream().filter(x -> { System.out.println(x); return true; });
// не напечатается ничего: терминальной операции нетЛенивость это не формальность, а рабочее свойство. Элементы проходят конвейер по одному, поэтому findFirst останавливается на первом подходящем, а limit(10) не заставляет обработать миллион элементов.
Ещё одно следствие: некоторые операции требуют полного прохода и потому копят состояние (sorted, distinct), а другие обрабатывают элемент за элементом. Смешивать их надо осознанно: sorted перед limit отсортирует всё, limit перед sorted только десять элементов.
Стрим одноразовый: после терминальной операции повторное использование даёт IllegalStateException.
Про peek стоит сказать отдельно: он задуман для отладки, а не для побочных эффектов, и оптимизатор вправе его не выполнить, если результат не нужен.
Где спотыкаются
Ждут выполнения от цепочки без терминальной операции.
Меняют внешнее состояние в map или peek.
Пытаются пройти по стриму дважды.
Ставят sorted до filter, сортируя лишнее.