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

Приведи пример терминальной операции

1.7 Middle🔥 241 комментариев
#SOLID и паттерны проектирования

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

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

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

Приведи пример терминальной операции

Терминальная операция (Terminal Operation) в Stream API — это операция, которая завершает поток и возвращает конечный результат. После терминальной операции больше нельзя работать со Stream.

Что такое терминальная операция

Операции в Stream API делятся на две категории:

  1. Промежуточные (Intermediate): возвращают Stream

    • map(), filter(), flatMap(), distinct(), sorted()
  2. Терминальные (Terminal): возвращают конкретный результат

    • collect(), forEach(), reduce(), findFirst(), count(), anyMatch()

Примеры терминальных операций

1. collect() — самая важная

List<String> names = Stream.of("Alice", "Bob", "Charlie")
    .filter(name -> name.length() > 3)
    .collect(Collectors.toList());  // Терминальная

System.out.println(names);  // [Alice, Charlie]
// Более сложный пример: группировка
Map<Integer, List<String>> byLength = Stream.of("a", "bb", "ccc", "dd")
    .collect(Collectors.groupingBy(String::length));

// Результат:
// {1=[a], 2=[bb, dd], 3=[ccc]}
// Создание Set вместо List
Set<String> uniqueNames = Stream.of("Alice", "Bob", "Alice")
    .collect(Collectors.toSet());

System.out.println(uniqueNames);  // [Alice, Bob]

2. forEach() — для выполнения действия

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

numbers.stream()
    .filter(n -> n % 2 == 0)
    .forEach(n -> System.out.println(n));  // Терминальная

// Вывод:
// 2
// 4

3. reduce() — для объединения элементов

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

// Суммирование
int sum = numbers.stream()
    .reduce(0, (a, b) -> a + b);  // Терминальная

System.out.println(sum);  // 15
// Найти максимум
int max = numbers.stream()
    .reduce(Integer.MIN_VALUE, (a, b) -> a > b ? a : b);

System.out.println(max);  // 5
// Optional версия (без начального значения)
Optional<Integer> sum = numbers.stream()
    .reduce((a, b) -> a + b);  // Терминальная

if (sum.isPresent()) {
    System.out.println("Sum: " + sum.get());  // Sum: 15
}

4. findFirst() / findAny() — поиск элемента

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

Optional<String> first = names.stream()
    .filter(name -> name.startsWith("B"))
    .findFirst();  // Терминальная - возвращает Optional

if (first.isPresent()) {
    System.out.println(first.get());  // Bob
}
// findAny() может быть быстрее в параллельных streams
Optional<String> any = names.parallelStream()
    .filter(name -> name.startsWith("A"))
    .findAny();  // Терминальная

System.out.println(any.get());  // Alice

5. count() — количество элементов

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

long evenCount = numbers.stream()
    .filter(n -> n % 2 == 0)
    .count();  // Терминальная

System.out.println(evenCount);  // 5

6. anyMatch() / allMatch() / noneMatch()

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

// anyMatch — есть ли хотя бы один?
boolean hasEven = numbers.stream()
    .anyMatch(n -> n % 2 == 0);  // Терминальная
System.out.println(hasEven);  // true

// allMatch — все ли?
boolean allPositive = numbers.stream()
    .allMatch(n -> n > 0);  // Терминальная
System.out.println(allPositive);  // true

// noneMatch — ни один?
boolean noNegative = numbers.stream()
    .noneMatch(n -> n < 0);  // Терминальная
System.out.println(noNegative);  // true

7. min() / max() — минимум и максимум

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

Optional<Integer> min = numbers.stream()
    .min(Integer::compareTo);  // Терминальная

Optional<Integer> max = numbers.stream()
    .max(Integer::compareTo);  // Терминальная

System.out.println("Min: " + min.get());  // Min: 1
System.out.println("Max: " + max.get());  // Max: 9

Практический пример: обработка данных пользователей

public class User {
    private String name;
    private int age;
    private String email;
    
    public User(String name, int age, String email) {
        this.name = name;
        this.age = age;
        this.email = email;
    }
    
    // getters...
}

// Данные
List<User> users = Arrays.asList(
    new User("Alice", 25, "alice@example.com"),
    new User("Bob", 30, "bob@example.com"),
    new User("Charlie", 35, "charlie@example.com"),
    new User("Diana", 28, "diana@example.com")
);

// Пример 1: найти первого пользователя старше 25
Optional<User> firstOlder = users.stream()
    .filter(u -> u.getAge() > 25)
    .findFirst();  // Терминальная

// Пример 2: все ли пользователи старше 20?
boolean allAdults = users.stream()
    .allMatch(u -> u.getAge() > 20);  // Терминальная
System.out.println(allAdults);  // true

// Пример 3: собрать имена пользователей старше 28
List<String> names = users.stream()
    .filter(u -> u.getAge() > 28)
    .map(User::getName)
    .collect(Collectors.toList());  // Терминальная
System.out.println(names);  // [Bob, Charlie]

// Пример 4: групп по возрасту
Map<Integer, List<User>> byAge = users.stream()
    .collect(Collectors.groupingBy(User::getAge));  // Терминальная

// Пример 5: среднее значение возраста
double avgAge = users.stream()
    .mapToInt(User::getAge)
    .average()  // Терминальная
    .orElse(0);
System.out.println("Average age: " + avgAge);  // Average age: 29.5

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

1. Stream не может использоваться после терминальной операции:

Stream<Integer> stream = Arrays.asList(1, 2, 3).stream();
stream.filter(n -> n > 1)
    .collect(Collectors.toList());  // Терминальная - Stream закрыт

stream.forEach(System.out::println);  // ОШИБКА! Stream already closed

2. Терминальная операция запускает обработку Stream:

// Это ничего не выполнит (нет терминальной операции)
Arrays.asList(1, 2, 3).stream()
    .filter(n -> {
        System.out.println("Filtering: " + n);  // Не выведется
        return n > 1;
    });

// А это выполнит
Arrays.asList(1, 2, 3).stream()
    .filter(n -> {
        System.out.println("Filtering: " + n);  // Выведется
        return n > 1;
    })
    .collect(Collectors.toList());  // Терминальная - запускает обработку

3. Порядок важен для производительности:

// Неэффективно: считает ВСЕ элементы, затем берёт первый
Optional<Integer> first = numbers.stream()
    .filter(n -> n > 5)
    .findFirst();

// Лучше: берёт первый и останавливается
Optional<Integer> first = numbers.stream()
    .filter(n -> {
        System.out.println("Checking: " + n);
        return n > 5;
    })
    .findFirst();
    
// Вывод: только первые несколько элементов, не все!

Итоги

Терминальные операции:

  • Завершают Stream и возвращают конечный результат
  • Запускают обработку (ленивые вычисления)
  • Основные: collect(), forEach(), reduce(), findFirst(), count(), anyMatch()
  • Нельзя использовать Stream после терминальной операции
  • Порядок операций важен для производительности