Терминальные методы
Терминальные методы
Как упоминалось ранее, терминальный метод запускает обработку всего потока. Рассмотрим основные из таких методов.
forEach(Consumer)
Этот метод не рекомендуется использовать в продакшене, так как он не возвращает результат. Это означает, что у него может быть только побочный эффект. Например, если мы начинаем собирать данные с помощью метода forEach(), а затем кто-то применяет параллельное выполнение к стриму, мы тут же столкнемся со всеми проблемами синхронизации.
public int getSum (Stream<Integer> s) {int [] sum = new int [1];s.forEach ( i -> sum [0] += i);return sum [0];}
сollect(Collector)
Этот метод часто используется в конвейере потока. Его применяют для сбора результата потока в определенную структуру: строку, коллекцию (List, Set, Map).
Метод принимает объект типа Collector в качестве аргумента, который определяет способ осуществления операции подсчета.
Класс Collector
Интерфейс Collector инкапсулирует процесс комбинирования элементов потока в одну итоговую структуру. Коллекторы можно использовать с различными методами потока, такими как collect(), groupingBy(), joining(), partitioningBy() и др.

Класс Collectors содержит набор статических методов-коллекторов, которые упрощают выполнение общих операций, таких как преобразование элементов в списки, множества и другие структуры данных.
Вот некоторые наиболее популярные методы класса Collectors:
toList(): Этот метод возвращает коллектор, который накапливает входные элементы в новый List.
toSet(): Этот метод возвращает коллектор, который накапливает входные элементы в новый Set.
joining(): Возвращает коллектор, который объединяет элементы потока в единую строку.
counting(): Возвращает коллектор, который подсчитывает количество элементов в потоке.
Вы можете быстро реализовать метод collect(Collector<? super T, A, R> collector) для сбора элементов в какую-то конкретную структуру.
Stream<?> stream;List<?> list = stream.collect(Collectors.toList());//Коллектор выше аналогичен данному кодуlist = stream.collect(() -> new ArrayList<>(), // определяем структуру(list, t) -> list.add(t), // определяем, как добавлять элементы(l1, l2) -> l1.addAll(l2) // и как объединять две структуры в одну);
reduce()
Метод reduce() применяется для комбинирования элементов потока в одно значение. Он отличается от метода collect() тем, что использует ассоциативную функцию, принимающую два значения и объединяющую их в одно. Например, метод reduce() можно использовать для суммирования чисел или для нахождения максимального или минимального числа.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);Optional<Integer> sum = numbers.stream().reduce((a, b) -> a + b);System.out.println(sum.get());
Optional<T> findFirst()
Метод findFirst() возвращает Optional<T> и служит для извлечения первого элемента из потока. Он обычно используется в сочетании с методом filter().
Optional<T> findAny()
Метод findAny() может оказаться полезным в ситуациях, когда вам нужно получить любой элемент из потока без конкретного предпочтения.
В отличие от findFirst(), который всегда возвращает первый найденный элемент потока, findAny(), при параллельном выполнении потока, может возвращать любой элемент, поскольку выбор элемента зависит от того, какой поток обработает его первым.
boolean anyMatch(Predicate)
Метод anyMatch(Predicate) используется для проверки, соответствует ли хотя бы один элемент потока указанному предикату.
boolean allMatch(Predicate)
Возвращает true, если все элементы потока удовлетворяют предикату.
Short-circuiting
Рассмотрим так называемые операции "короткого замыкания", которые прекращают обработку, как только находят нужный результат. Это значительно повышает производительность, особенно при работе с большими потоками данных. Примерами операций короткого замыкания могут служить методы anyMatch(), allMatch(), noneMatch(), findFirst(), findAny().
Важно отметить, что поведение операций короткого замыкания может изменяться в зависимости от того, является ли поток параллельным или последовательным. Например, в параллельном потоке метод findAny() может вернуть любой элемент, удовлетворяющий условию, вместо первого попавшегося, как это происходит в последовательном потоке.