Является ли forEach в Java Stream API конечным или промежуточным методом?

Ответ

forEach в Java Stream API является конечным (terminal) методом. Он завершает поток, выполняя действие для каждого элемента, и не возвращает новый Stream.

Пример:

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.stream()
     .filter(name -> name.startsWith("A"))
     .forEach(System.out::println); // Конечная операция

Ключевые особенности:

  • После forEach нельзя вызвать другие методы потока — это вызовет ошибку компиляции.
  • В параллельных стримах (parallelStream()) порядок выполнения не гарантируется. Для сохранения порядка используйте forEachOrdered.
  • Сам метод не изменяет исходную коллекцию, но действие внутри (например, вызов сеттера) может изменить состояние элементов.

Ответ 18+ 🔞

Ну, вот смотри, тут про forEach в стримах рассказывают, а я тебе на пальцах объясню, чтобы ты не охуел от этой документации.

Представь, что стрим — это конвейер с пивом. Ты его фильтруешь, сортируешь, делаешь с ним что хочешь. А forEach — это момент, когда ты, наконец, этот пивной бокальчик подносишь ко рту и делаешь глоток. Всё, процесс завершён, обратно в конвейер его не засунешь. Это и есть конечная операция, ёпта. После неё поток закрывается нахер.

Вот тебе пример, чтобы не быть голословным:

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.stream()
     .filter(name -> name.startsWith("A"))
     .forEach(System.out::println); // Конечная операция

Тут мы отфильтровали имена, и forEach их просто выплюнул в консоль. Всё, приехали.

А теперь главное, что надо запомнить, чтобы не выглядеть как манда с ушами:

  1. После forEach — тишина. Попробуй после него вызвать ещё какой-нибудь map или filter — компилятор тебе такую ошибку влепит, что ты сам от себя охуеешь. Поток уже умер, чувак, его не воскресить.
  2. Если работаешь с параллельными стримами (parallelStream()), то порядок выполнения — как бог на душу положит. Элементы могут обрабатываться в разнобой. Если тебе важен порядок, как в армии, то бери forEachOrdered. Он, правда, может быть медленнее, но зато всё по полочкам.
  3. Сам по себе forEach не тыкается в исходную коллекцию. Он не удалит оттуда элементы и не добавит новых. Но! Если внутри действия ты, например, вызываешь сеттер у объекта и меняешь его состояние — то да, объект изменится. Это как если бы ты, выпив пиво из кружки, нарисовал на ней усы. Кружка-то та же, но вид другой.

Короче, forEach — это точка, где всё заканчивается. Используй его, когда тебе нужно просто что-то сделать с каждым элементом и забыть. Для всего остального есть другие операции.