Что означает «ленивость» (lazy evaluation) Stream API в Java?

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

Ответ

Ленивость (lazy evaluation) означает, что промежуточные операции Stream (такие как filter, map, sorted) не выполняются немедленно. Они лишь формируют описание будущего конвейера обработки. Вычисления запускаются только при вызове терминальной операции (collect, forEach, count и т.д.).

Преимущества ленивости:

  • Оптимизация производительности: Поток может обрабатывать данные за один проход, объединяя операции и обрабатывая только необходимые элементы (например, при использовании limit()).
  • Возможность работы с бесконечными потоками: Например, с Stream.generate() или Stream.iterate().

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

List<String> names = Arrays.asList("Anna", "Bob", "Carol", "David");

Stream<String> stream = names.stream()
    .filter(name -> {
        System.out.println("[filter] " + name); // Отслеживаем вызов
        return name.length() > 3;
    })
    .map(name -> {
        System.out.println("[map] " + name); // Отслеживаем вызов
        return name.toUpperCase();
    });
// На этом этапе НИЧЕГО не выведется в консоль. Конвейер не выполнен.

List<String> result = stream.collect(Collectors.toList()); // Терминальная операция
// Теперь выполнится и выведет:
// [filter] Anna
// [map] Anna
// [filter] Bob
// [filter] Carol
// [map] Carol
// [filter] David
// [map] David

Обратите внимание: Обработка происходит поэлементно (Anna -> filter -> map, затем Bob -> filter и т.д.), а не сначала весь filter, потом весь map. Это еще одно следствие ленивости и оптимизации.