← Назад к вопросам

Что такое lazy evaluation в Stream?

2.0 Middle🔥 251 комментариев
#Stream API и функциональное программирование

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Lazy Evaluation в Stream

Lazy Evaluation (ленивое вычисление) — это фундаментальный принцип работы Stream API в Java, при котором промежуточные операции не выполняют работу немедленно, а накапливают описание преобразований. Реальное вычисление происходит только когда вызывается терминальная операция (terminal operation).

Противопоставление Eager vs Lazy

Eager Evaluation (в других коллекциях):

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> squared = new ArrayList<>();
for (int num : numbers) {
    squared.add(num * num);  // Вычисляется СРАЗУ
}

Lazy Evaluation (в Stream):

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> squared = numbers.stream()
    .map(n -> n * n);  // Ничего не вычисляется здесь!
// Вычисление происходит только при вызове терминальной операции
List<Integer> result = squared.collect(Collectors.toList());

Промежуточные операции (Intermediate Operations)

Это операции, которые возвращают новый Stream и ничего не вычисляют:

Stream<Integer> stream = numbers.stream()
    .filter(n -> n > 2)      // Не выполняется
    .map(n -> n * 2)         // Не выполняется
    .distinct();             // Не выполняется

// На этот момент работа не произведена!

Основные промежуточные операции:

  • filter() — фильтрация
  • map() — трансформация
  • flatMap() — плоское отображение
  • sorted() — сортировка
  • distinct() — удаление дубликатов
  • limit() — ограничение количества элементов
  • skip() — пропуск первых элементов
  • peek() — обход для отладки

Терминальные операции (Terminal Operations)

Это операции, которые запускают вычисление и возвращают конкретный результат:

List<Integer> result = numbers.stream()
    .filter(n -> n > 2)
    .map(n -> n * 2)
    .distinct()
    .collect(Collectors.toList());  // Здесь происходит ВСЯ работа!

Основные терминальные операции:

  • collect() — сбор результата
  • forEach() — итерация
  • reduce() — редукция
  • count() — подсчёт элементов
  • min(), max() — нахождение минимума/максимума
  • findFirst(), findAny() — поиск элемента
  • anyMatch(), allMatch(), noneMatch() — проверка условия
  • toArray() — преобразование в массив

Практический пример ленивого вычисления

public class LazyEvaluationExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        
        // Эта цепочка НЕ вычисляет ничего
        Stream<Integer> stream = numbers.stream()
            .filter(n -> {
                System.out.println("Filtering: " + n);
                return n > 3;
            })
            .map(n -> {
                System.out.println("Mapping: " + n);
                return n * 2;
            })
            .limit(2);
        
        System.out.println("No computation yet!");
        
        // Только здесь происходит вычисление
        List<Integer> result = stream.collect(Collectors.toList());
        System.out.println("Result: " + result);
    }
}

Вывод:

No computation yet!
Filtering: 1
Filtering: 2
Filtering: 3
Filtering: 4
Mapping: 4
Filtering: 5
Mapping: 5
Result: [8, 10]

Обратите внимание: filter применяется к каждому элементу, но map вызывается только для элементов, прошедших фильтр, и только до первых двух результатов (благодаря limit)!

Преимущества Lazy Evaluation

  1. Производительность — если используется limit(), ненужные элементы не обрабатываются
  2. Память — не создаётся промежуточные коллекции
  3. Работа с бесконечными потоками:
Stream<Integer> infiniteStream = Stream.iterate(0, n -> n + 1);
List<Integer> first10 = infiniteStream
    .limit(10)
    .collect(Collectors.toList());
  1. Short-circuit операцииfindFirst(), anyMatch() могут остановить обработку досрочно:
boolean anyLarge = numbers.stream()
    .filter(n -> n > 100)  // Может остановиться после нахождения
    .anyMatch(n -> true);   // первого совпадения

Важные моменты

  • Статelessность — операции map(), filter() должны быть без побочных эффектов
  • Неповторяемость — Stream можно использовать только один раз
  • peek() для отладки — для просмотра промежуточных значений
  • Параллельные потокиparallelStream() также использует ленивое вычисление

Lazy evaluation делает Stream API очень эффективным инструментом для обработки данных.

Что такое lazy evaluation в Stream? | PrepBro