Приведи пример результирующих функций в Stream API
Комментарии (3)
Ответ сгенерирован нейросетью и может содержать ошибки
Примеры терминальных (результирующих) операций в Stream API
Терминальные операции в Stream API (также называемые результирующими) — это операции, которые запускают обработку потока данных и возвращают конкретный результат (не Stream), завершая таким образом цепочку вызовов. После выполнения терминальной операции поток считается потреблённым и не может быть использован повторно. Вот ключевые примеры:
1. collect() – сбор элементов в коллекцию или агрегированный результат
Самый гибкий метод, часто используемый для преобразования потока в стандартные коллекции (List, Set, Map) или с помощью сложных коллекторов (Collectors).
// Сбор в List
List<String> names = Stream.of("Анна", "Иван", "Мария", "Пётр")
.filter(name -> name.length() > 4)
.collect(Collectors.toList()); // Результат: ["Анна", "Мария", "Пётр"]
// Сбор в Set (уникальные значения)
Set<Integer> numbers = Stream.of(1, 2, 2, 3, 4, 4)
.collect(Collectors.toSet()); // Результат: [1, 2, 3, 4]
// Сбор в Map (ключ-значение)
Map<String, Integer> nameLengthMap = Stream.of("Анна", "Иван")
.collect(Collectors.toMap(
name -> name, // keyMapper
String::length // valueMapper
)); // Результат: {"Анна"=4, "Иван"=4}
// Группировка по критерию (очень мощный коллектор!)
Map<Integer, List<String>> groupedByLength = Stream.of("Аня", "Иван", "Ольга")
.collect(Collectors.groupingBy(String::length));
// Результат: {3=["Аня"], 4=["Иван", "Ольга"]}
2. forEach() – выполнение действия для каждого элемента
Используется для побочных эффектов (например, логирование, запись в файл). Не рекомендуется для модификации внешних переменных в параллельных потоках.
// Простой вывод в консоль
Stream.of("Java", "Python", "C++")
.forEach(System.out::println);
// Более сложное действие
List<String> resultList = new ArrayList<>();
Stream.of("data1", "data2", "data3")
.forEach(resultList::add); // Добавление в внешнюю коллекцию
3. toArray() – преобразование в массив
Полезно при необходимости совместимости со старым кодом, работающим с массивами.
// Преобразование в массив Object[]
Object[] objects = Stream.of(1, 2, 3).toArray();
// Преобразование в типизированный массив с помощью функции-генератора
Integer[] integers = Stream.of(1, 2, 3)
.toArray(Integer[]::new); // Результат: [1, 2, 3]
4. Методы агрегации: count(), min(), max(), sum(), average()
// count() – количество элементов
long count = Stream.of(1, 2, 3, 4, 5)
.filter(n -> n % 2 == 0)
.count(); // Результат: 2
// min()/max() – минимальный/максимальный элемент (требуют Comparator)
Optional<Integer> min = Stream.of(5, 2, 8, 1)
.min(Integer::compareTo); // Результат: Optional[1]
// sum() и average() доступны для числовых потоков (IntStream, LongStream, DoubleStream)
double average = IntStream.of(10, 20, 30)
.average() // Возвращает OptionalDouble
.orElse(0.0); // Результат: 20.0
5. reduce() – свёртка потока к одному значению
Базовый метод для реализации собственной агрегации, аналогичный операции "fold" в функциональных языках.
// Сумма чисел
Optional<Integer> sum = Stream.of(1, 2, 3, 4)
.reduce((a, b) -> a + b); // Результат: Optional[10]
// С начальным значением (identity)
Integer product = Stream.of(1, 2, 3, 4)
.reduce(1, (a, b) -> a * b); // Результат: 24
// Более сложная свёртка: конкатенация строк с разделителем
String concatenated = Stream.of("a", "b", "c")
.reduce("", (partial, element) ->
partial + (partial.isEmpty() ? "" : "-") + element
); // Результат: "a-b-c"
6. Методы поиска и проверки: findFirst(), findAny(), anyMatch(), allMatch(), noneMatch()
// Поиск первого элемента (важно для ordered streams)
Optional<String> first = Stream.of("a", "b", "c")
.findFirst(); // Результат: Optional["a"]
// Проверка условий
boolean hasEven = Stream.of(1, 3, 5, 7, 8)
.anyMatch(n -> n % 2 == 0); // Результат: true (есть хотя бы одно чётное)
boolean allPositive = Stream.of(1, 2, 3)
.allMatch(n -> n > 0); // Результат: true (все положительные)
boolean noNegative = Stream.of(1, 2, 3)
.noneMatch(n -> n < 0); // Результат: true (нет отрицательных)
7. Итераторы и сплитераторы
Для интеграции с legacy-кодом или ручной обработки.
// Получение итератора
Iterator<String> iterator = Stream.of("a", "b", "c").iterator();
// Получение сплитератора (для параллельной обработки)
Spliterator<String> spliterator = Stream.of("a", "b", "c").spliterator();
Ключевые особенности терминальных операций:
- Ленивое выполнение – обработка начинается только при вызове терминальной операции
- Потребление потока – после вызова терминальной операции поток нельзя повторно использовать
- Оптимизация – многие операции (
count(),min()/max()для отсортированных потоков) имеют оптимизации - Короткое замыкание – операции
findFirst(),anyMatch()могут завершиться досрочно
Выбор конкретной терминальной операции зависит от задачи: нужна ли коллекция (collect()), агрегированное значение (reduce(), sum()), проверка условия (anyMatch()) или просто побочный эффект (forEach()). Для большинства сценариев преобразования данных collect() с использованием богатого набора Collectors является наиболее универсальным и выразительным инструментом.