Какие типы операций со стримами существуют
Что проверяют
Знаете ли вы деление на промежуточные и терминальные операции и понимаете ли ленивость: пока нет терминальной операции, стрим ничего не делает. Следом обычно спрашивают, в каком порядке выполнятся filter и map для элементов и что будет при повторном использовании стрима.
Как отвечать
Две основные группы:
- промежуточные (intermediate): возвращают новый
Streamи только описывают конвейер. Без состояния:filter,map,flatMap,peek,mapMulti(Java 16). С состоянием:sorted,distinct, им нужно увидеть много элементов или все сразу. Сюда же относятсяlimit,skip,takeWhile,dropWhile; - терминальные (terminal): запускают обработку и закрывают стрим.
collect,reduce,forEach,count,min,max,toList()(Java 16),findFirst,anyMatch.
Сильный ответ добавляет третье измерение:
- короткозамкнутые (short-circuiting): могут закончить работу, не дочитав источник. Это
limit,findFirst,anyMatch,allMatch,noneMatch. Благодаря им работает бесконечныйStream.iterate(...).limit(10).
Про ленивость скажите, что элементы идут по конвейеру по одному: первый элемент проходит filter и map, потом второй, а не весь список сначала через filter, затем через map. После терминальной операции стрим использовать нельзя, будет IllegalStateException.
Для свежей Java упомяните Stream Gatherers: метод gather() позволяет писать собственные промежуточные операции, в JDK 24 они вышли из превью. Параллельный режим включается через parallelStream() или parallel() и по умолчанию использует общий ForkJoinPool.
Где спотыкаются
Вешают логику на peek. Это отладочная операция, и начиная с Java 9 count() может вовсе не прогнать элементы через конвейер, если размер известен из источника.
Путают toList() и Collectors.toList(): первый возвращает неизменяемый список, и add на нём бросит UnsupportedOperationException.
Считают parallelStream() бесплатным ускорением. На маленьких коллекциях, с блокирующими вызовами или с общим изменяемым состоянием он работает медленнее или даёт неверный результат.