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

Что такое нетерминальная операция в Stream API?

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

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

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

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

Нетерминальные операции в Stream API

Non-Terminal операции (промежуточные операции) — это методы, которые преобразуют Stream и возвращают новый Stream. Они не выполняют вычисления, пока не будет вызвана терминальная операция (это называется "ленивое вычисление").

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

1. filter() — фильтрация элементов

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);

Stream<Integer> filtered = numbers.stream()
    .filter(n -> n > 3);  // Нетерминальная операция

// До сих пор ничего не вычислено!
// Вычисление произойдет только при вызове терминальной операции

List<Integer> result = filtered.collect(Collectors.toList());  // Терминальная
System.out.println(result);  // [4, 5, 6]

2. map() — преобразование элементов

List<String> words = Arrays.asList("Hello", "World");

List<Integer> lengths = words.stream()
    .map(String::length)  // Нетерминальная: преобразует в длины
    .collect(Collectors.toList());  // Терминальная

System.out.println(lengths);  // [5, 5]

3. flatMap() — раскрытие вложенных потоков

List<List<Integer>> lists = Arrays.asList(
    Arrays.asList(1, 2),
    Arrays.asList(3, 4),
    Arrays.asList(5, 6)
);

List<Integer> flattened = lists.stream()
    .flatMap(List::stream)  // Нетерминальная: раскрывает вложенные списки
    .collect(Collectors.toList());  // Терминальная

System.out.println(flattened);  // [1, 2, 3, 4, 5, 6]

4. distinct() — удаление дубликатов

List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 3, 3);

List<Integer> unique = numbers.stream()
    .distinct()  // Нетерминальная
    .collect(Collectors.toList());  // Терминальная

System.out.println(unique);  // [1, 2, 3]

5. sorted() — сортировка

List<Integer> numbers = Arrays.asList(5, 2, 8, 1, 9);

List<Integer> sorted = numbers.stream()
    .sorted()  // Нетерминальная
    .collect(Collectors.toList());  // Терминальная

System.out.println(sorted);  // [1, 2, 5, 8, 9]

// С компаратором
List<String> words = Arrays.asList("hello", "world", "java");
List<String> sortedByLength = words.stream()
    .sorted(Comparator.comparingInt(String::length))
    .collect(Collectors.toList());

6. peek() — просмотр элементов (для отладки)

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

List<Integer> result = numbers.stream()
    .filter(n -> n > 2)
    .peek(n -> System.out.println("Элемент: " + n))  // Выведет при выполнении
    .map(n -> n * 2)
    .peek(n -> System.out.println("После умножения: " + n))
    .collect(Collectors.toList());  // Терминальная

7. limit() — ограничение количества

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

List<Integer> first3 = numbers.stream()
    .limit(3)  // Нетерминальная: оставляет первые 3
    .collect(Collectors.toList());  // Терминальная

System.out.println(first3);  // [1, 2, 3]

8. skip() — пропуск элементов

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

List<Integer> skipFirst2 = numbers.stream()
    .skip(2)  // Нетерминальная: пропускает первые 2
    .collect(Collectors.toList());  // Терминальная

System.out.println(skipFirst2);  // [3, 4, 5]

Ленивое вычисление (Lazy Evaluation)

Это ключевая особенность нетерминальных операций:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

numbers.stream()
    .map(n -> {
        System.out.println("Преобразую: " + n);
        return n * 2;
    })  // Нетерминальная — ничего не выполняется!
    .filter(n -> n > 4)  // Нетерминальная — ничего не выполняется!
    // Если здесь остановиться, вывода не будет
    .forEach(System.out::println);  // Терминальная — ТУТ выполняются все!

/* Вывод:
Преобразую: 1
Преобразую: 2
Преобразую: 3
Преобразую: 4
Преобразую: 5
8
10
*/

Цепь операций с нетерминальными методами

List<String> words = Arrays.asList(
    "apple", "banana", "apricot", "avocado", "blueberry"
);

List<String> result = words.stream()
    .filter(w -> w.length() > 4)      // Нетерминальная: фильтр
    .map(String::toUpperCase)         // Нетерминальная: преобразование
    .sorted()                         // Нетерминальная: сортировка
    .distinct()                       // Нетерминальная: удаление дубликатов
    .limit(3)                         // Нетерминальная: ограничение
    .collect(Collectors.toList());    // Терминальная: выполнить все

System.out.println(result);  // [APPLE, APRICOT, AVOCADO]

Сравнение: Нетерминальные vs Терминальные операции

ОперацияТипВозвратВыполнение
filter, map, flatMapНетерминальнаяStreamЛенивое
sorted, distinct, limitНетерминальнаяStreamЛенивое
forEach, collect, reduceТерминальнаяРезультат/voidНемедленное
count, findFirst, anyMatchТерминальнаяРезультатНемедленное

Практический пример

public class UserFilter {
    public static void main(String[] args) {
        List<User> users = Arrays.asList(
            new User("John", 25),
            new User("Jane", 30),
            new User("Bob", 22),
            new User("Alice", 28)
        );
        
        List<String> result = users.stream()
            .filter(u -> u.getAge() > 23)        // Нетерминальная
            .map(User::getName)                  // Нетерминальная
            .sorted()                            // Нетерминальная
            .collect(Collectors.toList());       // Терминальная
        
        System.out.println(result);  // [Alice, Jane, John]
    }
}

Почему это важно

  • Производительность: ленивое вычисление позволяет остановиться рано (например, при limit())
  • Читаемость: цепь методов выглядит как описание трансформации
  • Комбинируемость: легко комбинировать несколько операций
  • Функциональный стиль: соответствует парадигме функционального программирования
Что такое нетерминальная операция в Stream API? | PrepBro