Когда выполнение операций начинается в Java Stream API?

«Когда выполнение операций начинается в Java Stream API?» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Выполнение операций в Java Stream API начинается только при вызове терминальной операции (terminal operation). Промежуточные операции (intermediate operations) лишь конструируют конвейер и являются "ленивыми" (lazy).

Почему это важно: Это позволяет оптимизировать обработку (например, short-circuiting) и не тратить ресурсы на вычисления, если результат не требуется.

Пример, демонстрирующий ленивое выполнение:

import java.util.List;
import java.util.stream.Stream;

public class StreamLazyEvaluation {
    public static void main(String[] args) {
        List<String> data = List.of("A", "B", "C");

        // Создание потока и добавление промежуточных операций
        Stream<String> stream = data.stream()
                .peek(s -> System.out.println("До фильтра: " + s)) // Не выполнится сейчас
                .filter(s -> {
                    System.out.println("Фильтр: " + s); // Не выполнится сейчас
                    return s.startsWith("A");
                })
                .map(s -> {
                    System.out.println("Маппинг: " + s); // Не выполнится сейчас
                    return s.toLowerCase();
                });

        System.out.println("Поток создан, но обработка еще не началась.");

        // ВЫЗОВ ТЕРМИНАЛЬНОЙ ОПЕРАЦИИ — здесь начинается реальная обработка
        long count = stream.count(); // Операции peek, filter, map выполнятся здесь
        System.out.println("Количество элементов: " + count);
    }
}

Вывод: Без вызова count(), collect(), forEach() и т.д. поток не обработает ни одного элемента.