Что такое Stream API в Java и каковы его основные особенности?

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

Ответ

Stream API (пакет java.util.stream) — это абстракция для декларативной и функциональной обработки последовательностей данных (коллекций, массивов, I/O каналов), представленная в Java 8.

Ключевые концепции:

  • Не хранит данные. Stream — это конвейер операций над источником данных.
  • Не изменяет источник. Все операции возвращают новый Stream, оставляя исходную коллекцию неизменной.
  • Ленивые (lazy) вычисления. Промежуточные операции (filter, map) выполняются только при вызове терминальной операции.
  • Однократная трассировка. Stream нельзя использовать повторно после вызова терминальной операции.

Типы операций:

  1. Промежуточные (Intermediate): Возвращают Stream<T>.
    • filter(Predicate<T>) — фильтрация.
    • map(Function<T, R>) — преобразование.
    • sorted(), distinct(), limit(n), skip(n).
  2. Терминальные (Terminal): Запускают выполнение конвейера и возвращают результат или void.
    • collect(Collector) — агрегация в коллекцию.
    • forEach(Consumer) — выполнение действия.
    • reduce() — свертка к одному значению.
    • count(), anyMatch(), findFirst().

Примеры:

// 1. Фильтрация и преобразование
List<String> names = List.of("Anna", "Bob", "Alex", "Alice");
List<String> result = names.stream()
        .filter(name -> name.startsWith("A")) // Промежуточная
        .map(String::toUpperCase)             // Промежуточная
        .sorted()
        .collect(Collectors.toList());        // Терминальная
// result = [ALEX, ALICE, ANNA]

// 2. Работа с примитивами (IntStream, LongStream)
int sum = IntStream.range(1, 10) // 1..9
        .filter(x -> x % 2 == 0)
        .sum(); // sum = 20

// 3. Параллельная обработка
long count = largeList.parallelStream() // Использует ForkJoinPool
        .filter(item -> item.isValid())
        .count();

Важные особенности:

  • Short-circuit операции: limit(), findFirst(), anyMatch() могут завершить обработку досрочно.
  • Порядок в parallel stream: Не гарантируется, если только не используется forEachOrdered().
  • Использование с Optional: Многие терминальные операции возвращают Optional (например, findFirst()).