← Назад к вопросам
Что такое нетерминальная операция в 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())
- Читаемость: цепь методов выглядит как описание трансформации
- Комбинируемость: легко комбинировать несколько операций
- Функциональный стиль: соответствует парадигме функционального программирования