Для чего нужен метод peek в Stream API в Java
Что проверяют
Понимаете ли вы ленивость потоков и знаете ли, что peek не гарантирует вызов для каждого элемента. Вопрос-ловушка.
Как отвечать
peek это промежуточная операция, которая передаёт элемент дальше без изменений и попутно выполняет над ним действие. Задумана она для отладки: посмотреть, что проходит через определённый этап конвейера.
Ключевые оговорки, ради которых вопрос и задают:
- поток ленив: без завершающей операции ничего не выполнится вовсе, и
peekне сработает ни разу; peekможет не вызваться для всех элементов: если завершающая операция позволяет пропустить обход (например,count()на потоке, где размер известен, илиfindFirstс коротким замыканием), реализация вправе не выполнятьpeek. Это прямо оговорено в документации;- побочные эффекты в
peekэто плохая практика: изменять состояние или коллекции оттуда нельзя, для этого естьforEachв конце конвейера; - в параллельном потоке порядок вызовов непредсказуем, и вывод в лог перемешается.
List<String> titles = jobs.stream()
.filter(Job::isActive)
.peek(j -> log.debug("прошло фильтр: {}", j.id())) // только для отладки
.map(Job::title)
.toList();Практический вывод: peek уместен во временной отладке и в логировании при разборе проблемы, но не как часть логики. Если действие обязано выполниться для каждого элемента, ему место в forEach или в map с явным возвратом.
Где спотыкаются
Собирают побочные эффекты в peek и удивляются, что часть элементов пропущена.
Ставят peek и забывают завершающую операцию, а потом ищут, почему в логах пусто.
Оставляют отладочный peek в боевом коде и получают лишнюю работу на каждом элементе.