Ответ
Операция reduce в Java Stream API используется для свертки (агрегации) элементов потока в одно результирующее значение. Это терминальная операция.
Основные формы reduce:
-
T reduce(T identity, BinaryOperator<T> accumulator)identity— начальное значение и значение по умолчанию для пустого потока.accumulator— функция, которая объединяет текущий результат с очередным элементом.
// Сумма чисел List<Integer> numbers = List.of(1, 2, 3, 4); int sum = numbers.stream().reduce(0, (a, b) -> a + b); // Результат: 10 // Эквивалентно: 0 + 1 = 1, 1 + 2 = 3, 3 + 3 = 6, 6 + 4 = 10 -
Optional<T> reduce(BinaryOperator<T> accumulator)- Используется, когда нет естественного начального значения (identity). Возвращает
Optional, так как поток может быть пустым.
// Поиск максимального значения Optional<Integer> max = numbers.stream().reduce(Integer::max); max.ifPresent(System.out::println); // Выведет 4 - Используется, когда нет естественного начального значения (identity). Возвращает
-
<U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)- Наиболее общая форма, нужна для параллельных стримов.
combinerобъединяет частичные результаты, полученные в разных потоках.
// Конкатенация строк в параллельном потоке List<String> words = List.of("Hello", " ", "World", "!"); String sentence = words.parallelStream() .reduce("", (partialString, element) -> partialString + element, String::concat); System.out.println(sentence); // "Hello World!" - Наиболее общая форма, нужна для параллельных стримов.
Практические примеры и нюансы:
- Используйте ссылки на методы:
Integer::sum,Integer::maxвместо лямбд. identityдолжен быть нейтральным элементом: Для сложения — 0, для умножения — 1. Иначе результат в параллельных стримах будет некорректен.- Альтернативы
reduce: Часто более удобны специализированные операцииsum(),min(),max(),collect().reduceхорош для кастомной логики агрегации.
// Кастомная агрегация: объединение объектов
List<Item> items = ...;
Basket totalBasket = items.stream()
.reduce(new Basket(), // identity - пустая корзина
(basket, item) -> basket.add(item), // accumulator
Basket::merge); // combiner для параллельного выполнения Ответ 18+ 🔞
Да ты посмотри на эту штуку, reduce! Ну прям как тот самый Герасим, блядь, из рассказа — молчаливый, здоровый, а внутри ебать какая мощь скрыта! Собирает всё в кучу, как тот немой в мешок, и выдаёт один результат. Красота, ёпта!
Вот, смотри, какие у него три основные формы, как три богатыря, блядь:
-
T reduce(T identity, BinaryOperator<T> accumulator)Это как прийти в магазин с деньгами, сука.identity— это твоя начальная сумма в кармане, даже если ты нихуя не купил, она у тебя есть. Аaccumulator— это кассирша, которая к твоей сумме прибавляет цену каждой новой хуйни из корзины.// Считаем бабки, потраченные на пиво List<Integer> ценыНаПиво = List.of(100, 150, 200, 80); int итоговаяАгония = ценыНаПиво.stream().reduce(0, (ужеНабрал, ещёБутылка) -> ужеНабрал + ещёБутылка); // Итог: 530 // Считаем: 0 + 100 = 100, 100 + 150 = 250, 250 + 200 = 450, 450 + 80 = 530. Пиздец, дорого. -
Optional<T> reduce(BinaryOperator<T> accumulator)А это форма для максималистов, как тот самый Герасим, блядь! Начального значения нет — либо найдёшь что-то в потоке, либо нихуя. ВозвращаетOptional, потому что поток может быть пустым, как твои карманы после той же кальянной.// Ищем самую дорогую бутылку в баре (максимальное значение) Optional<Integer> самаяДорогаяПосудина = ценыНаПиво.stream().reduce(Integer::max); самаяДорогаяПосудина.ifPresent(цену -> System.out.println("А эта стоила: " + цену)); // Выведет: 200 -
<U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)О, это уже для распараллеливания, когда задач до овердохуища!identity— начальное состояние,accumulator— как каждый поток по отдельности копит свой результат, аcombiner— это главный, который потом сводит все эти кучи в одну большую кучу. Без него в параллельных стримах — пиздец и хаос.// Собираем предложение из слов, и пусть каждый поток поработает List<String> слова = List.of("Ну", "чё", ",", "пошли", ",", "что", "ли", "?"); String предложение = слова.parallelStream() .reduce("", (частьФразы, слово) -> частьФразы + слово, String::concat); // combiner склеивает куски System.out.println(предложение); // "Ну чё, пошли, что ли?"
А теперь, блядь, практические советы, чтобы не облажаться:
- Используй ссылки на методы, не выёбывайся с лямбдами: Вместо
(a, b) -> a + bпишиInteger::sum. Красиво и понятно, как слон в посудной лавке. identityдолжен быть нейтральным, как швейцарец! Для сложения — 0, для умножения — 1. Если начнёшь с 10, то в параллельном стриме каждый поток начнёт с 10, и в итоге насчитаешь хуй знает что. Получишь не сумму, а пиздопроёбину.- Не забывай про альтернативы! Часто проще использовать готовые операции:
sum(),min(),collect().reduce— это когда тебе нужна своя, особенная, ебучка-аггрегация, которую стандартные методы не делают.
// Допустим, у нас список покупок, и мы хотим собрать одну большую тележку
List<Покупка> покупки = ...;
Тележка общаяТележка = покупки.stream()
.reduce(new Тележка(), // identity - пустая тележка
(тележка, штука) -> тележка.положить(штука), // кладём по одной штуке
Тележка::объединить); // а это combiner, если будем собирать параллельно
Вот и вся магия, блядь. Кажется сложно, но если вникнуть — проще пареной репы. Главное — не пытайся сразу всё понять, а то мозг ебнешь, как Герасим ту Муму. Потихоньку, сука, сориентируйся.