← Назад к вопросам
Что такое терминальная операция?
2.0 Middle🔥 241 комментариев
#Stream API и функциональное программирование
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Терминальные операции в Java Stream API
Терминальная операция — это завершающая операция в цепи потока (Stream), которая запускает обработку элементов и возвращает результат.
Основное различие
// Промежуточные операции (Intermediate)
Stream<Integer> stream = numbers.stream()
.filter(n -> n > 5) // filter() — промежуточная
.map(n -> n * 2) // map() — промежуточная
// Вычисления ещё НЕ произошли!
// Это просто цепь инструкций
// Терминальная операция запускает вычисления
List<Integer> result = stream.collect(Collectors.toList()); // collect() — терминальная
// Теперь произошло реальное выполнение!
Ключевые свойства терминальных операций
- Запускают вычисления — без терминальной операции ничего не происходит
- Возвращают конкретный результат — не Stream
- После них нельзя добавлять операции — поток закрыт
- Потребляют элементы — элементы обрабатываются по одному
Основные терминальные операции
1. forEach() — обход элементов
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// forEach() — терминальная, ничего не возвращает (void)
names.stream()
.filter(n -> n.length() > 3)
.forEach(System.out::println); // Печатает: Alice, Charlie
// Эквивалентно:
for (String name : names) {
if (name.length() > 3) {
System.out.println(name);
}
}
2. collect() — сбор результатов
// Сбор в List
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
// Сбор в Set
Set<Integer> uniqueNumbers = numbers.stream()
.collect(Collectors.toSet());
// Сбор в Map
Map<Integer, String> idToName = people.stream()
.collect(Collectors.toMap(
Person::getId, // ключ
Person::getName // значение
));
// Сбор с дополнительной логикой
Map<String, List<Person>> byCity = people.stream()
.collect(Collectors.groupingBy(
Person::getCity, // группировка по городу
Collectors.toList() // результат — список людей в каждом городе
));
// Пример результата:
// {Moscow: [Alice, Bob], SPb: [Charlie]}
3. reduce() — свёртка данных
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// Сумма всех чисел
int sum = numbers.stream()
.reduce(0, (acc, num) -> acc + num); // 15
// Пример пошагово:
// acc=0, num=1 → 0+1 = 1
// acc=1, num=2 → 1+2 = 3
// acc=3, num=3 → 3+3 = 6
// acc=6, num=4 → 6+4 = 10
// acc=10, num=5 → 10+5 = 15
// Произведение чисел
int product = numbers.stream()
.reduce(1, (acc, num) -> acc * num); // 120
// Optional версия (если поток пуст)
Optional<Integer> max = numbers.stream()
.reduce((a, b) -> a > b ? a : b);
if (max.isPresent()) {
System.out.println("Max: " + max.get());
}
4. count() — количество элементов
long evenCount = numbers.stream()
.filter(n -> n % 2 == 0)
.count(); // 5
// Эквивалентно:
long count = 0;
for (int num : numbers) {
if (num % 2 == 0) count++;
}
5. findFirst(), findAny() — поиск элемента
Optional<Integer> first = numbers.stream()
.filter(n -> n > 5)
.findFirst(); // Optional содержит первый элемент > 5
if (first.isPresent()) {
System.out.println(first.get());
}
// Альтернатива
int firstValue = numbers.stream()
.filter(n -> n > 5)
.findFirst()
.orElse(-1); // -1 если не найден
// findAny() — может вернуть любой элемент
Optional<Integer> any = numbers.stream()
.filter(n -> n > 5)
.findAny();
6. anyMatch(), allMatch(), noneMatch() — проверка условия
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// Есть ли хотя бы один элемент > 3?
boolean hasLarge = numbers.stream()
.anyMatch(n -> n > 3); // true
// Все ли элементы > 0?
boolean allPositive = numbers.stream()
.allMatch(n -> n > 0); // true
// Нет ли элементов < 0?
boolean noNegative = numbers.stream()
.noneMatch(n -> n < 0); // true
// Реальный пример
List<User> users = userRepository.findAll();
boolean allActive = users.stream()
.allMatch(User::isActive); // Все ли пользователи активны?
7. min(), max() — минимум и максимум
Optional<Integer> min = numbers.stream()
.min(Integer::compare); // Optional.of(1)
Optional<Integer> max = numbers.stream()
.max(Integer::compare); // Optional.of(5)
// С объектами
Optional<Person> youngest = people.stream()
.min(Comparator.comparingInt(Person::getAge));
if (youngest.isPresent()) {
System.out.println("Youngest: " + youngest.get().getName());
}
Полный практический пример
class User {
private Long id;
private String name;
private String city;
private int age;
// getters...
}
// Сложный запрос с несколькими промежуточными операциями
// и одной терминальной
var result = users.stream()
// Промежуточные операции
.filter(u -> u.getAge() > 18) // Фильтр
.filter(u -> "Moscow".equals(u.getCity())) // Ещё фильтр
.map(u -> u.getName().toUpperCase()) // Преобразование
.distinct() // Удаление дубликатов
.sorted() // Сортировка
// Терминальная операция — запускает все вычисления
.collect(Collectors.toList()); // Сбор в List
System.out.println(result);
// Вывод: [ALICE, BOB, CHARLIE]
Важная особенность: Ленивое вычисление
// Это НЕ выполняется до терминальной операции!
Stream<Integer> stream = numbers.stream()
.filter(n -> {
System.out.println("Checking: " + n); // Не печатается!
return n > 3;
})
.map(n -> {
System.out.println("Mapping: " + n); // Не печатается!
return n * 2;
});
// Теперь выполняется (с терминальной операцией)
List<Integer> result = stream.collect(Collectors.toList());
// Печатает:
// Checking: 1, 2, 3, 4, 5
// Mapping: 4, 5
Сравнение терминальных и промежуточных
| Операция | Тип | Возвращает | Пример |
|---|---|---|---|
| filter | Промежуточная | Stream | .filter(x > 5) |
| map | Промежуточная | Stream | .map(x -> x * 2) |
| flatMap | Промежуточная | Stream | .flatMap(x -> x.getChildren()) |
| distinct | Промежуточная | Stream | .distinct() |
| sorted | Промежуточная | Stream | .sorted() |
| forEach | Терминальная | void | .forEach(System.out::println) |
| collect | Терминальная | Collection | .collect(toList()) |
| reduce | Терминальная | Optional/Value | .reduce(0, Integer::sum) |
| count | Терминальная | long | .count() |
| findFirst | Терминальная | Optional | .findFirst() |
| anyMatch | Терминальная | boolean | .anyMatch(x > 5) |
Вывод
Терминальная операция — это завершение конвейера Stream'а, которое:
- Запускает обработку элементов
- Возвращает конкретный результат
- Закрывает поток (больше нельзя добавлять операции)
Без терминальной операции — это просто описание плана, а не выполнение.