Когда выполняются промежуточные (intermediate) операции в Java Stream API?

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

Ответ

Промежуточные операции (filter, map, sorted и т.д.) выполняются лениво (lazy). Это означает, что они не производят немедленных вычислений, а лишь конструируют новый Stream, описывающий конвейер операций.

Выполнение происходит только при вызове терминальной операции (collect, forEach, reduce, count и др.). При этом обработка элементов происходит поэлементно и последовательно по цепочке.

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

List<String> letters = List.of("a", "b", "c", "d");

Stream<String> stream = letters.stream()
    .peek(s -> System.out.println("Источник: " + s)) // Промежуточная
    .filter(s -> {
        System.out.println("  filter: " + s);        // Промежуточная
        return s.compareTo("b") > 0;
    })
    .map(s -> {
        System.out.println("    map: " + s);         // Промежуточная
        return s.toUpperCase();
    });
// На этом этапе НИЧЕГО не выведется — поток не активирован.

System.out.println("--- Вызов терминальной операции ---");
List<String> result = stream.collect(Collectors.toList()); // Активация

Вывод программы:

--- Вызов терминальной операции ---
Источник: a
  filter: a
Источник: b
  filter: b
Источник: c
  filter: c
    map: c
Источник: d
  filter: d
    map: d

Ключевой вывод: Элементы обрабатываются один за другим, проходя весь конвейер операций, и только по требованию терминального метода.