Как использовать метод reduce() в Java Stream API?

Ответ

Метод reduce() в Stream API используется для свертки элементов потока в одно итоговое значение с помощью ассоциативной функции.

Основные формы:

  1. Optional<T> reduce(BinaryOperator<T> accumulator)
  2. T reduce(T identity, BinaryOperator<T> accumulator)
  3. U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)

Примеры:

  • Суммирование чисел (с identity):

    List<Integer> numbers = List.of(1, 2, 3, 4, 5);
    int sum = numbers.stream()
                     .reduce(0, (a, b) -> a + b); // Идентичность 0 гарантирует результат int, а не Optional
    System.out.println(sum); // 15

    Или с методом-ссылкой:

    int sum = numbers.stream()
                     .reduce(0, Integer::sum);
  • Поиск максимального значения (без identity):

    Optional<Integer> max = numbers.stream()
                                   .reduce(Integer::max);
    max.ifPresent(System.out::println); // 5
  • Конкатенация строк:

    List<String> words = List.of("Hello", " ", "World", "!");
    String sentence = words.stream()
                           .reduce("", String::concat);
    System.out.println(sentence); // "Hello World!"

Важно: Функция-аккумулятор должна быть ассоциативной, чтобы корректно работать в параллельных потоках.

Ответ 18+ 🔞

Слушай, а вот этот ваш reduce() в стримах — это ж такая, блядь, классическая свертка, как в функциональщине! Берёшь кучу элементов и плющишь их в одно-единственное значение, как будто хуй с горы скатился и всё на своём пути собрал.

Смотри, какие у него три основные позы есть:

  1. Optional<T> reduce(BinaryOperator<T> accumulator) — начнём без начального значения, на авось, поэтому результат в Optional заворачивают, а то вдруг поток пустой, и нихуя не получится.
  2. T reduce(T identity, BinaryOperator<T> accumulator) — тут уже даём стартовое значение (identity), и тогда даже из пустого потока вылезет эта самая identity. Удобно, блядь.
  3. U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner) — это уже для продвинутых, когда тип результата отличается от типа элементов. Третий аргумент (combiner) — для параллельных стримов, чтобы куски склеивать, но в последовательных его обычно игнорят, ёпта.

Ну и примеры, чтобы не быть пиздаболом:

  • Чисто сложить все цифры (со стартовым нулём):

    List<Integer> numbers = List.of(1, 2, 3, 4, 5);
    int sum = numbers.stream()
                     .reduce(0, (a, b) -> a + b); // Берём ноль и начинаем накручивать
    System.out.println(sum); // 15

    А можно и покрасивее, через ссылку на метод:

    int sum = numbers.stream()
                     .reduce(0, Integer::sum); // Та же хуйня, только компактнее
  • Найти максимум (без начального значения, на свой страх и риск):

    Optional<Integer> max = numbers.stream()
                                   .reduce(Integer::max); // А вдруг пустой список? Optional нас спасает!
    max.ifPresent(System.out::println); // 5
  • Склеить строки в одну:

    List<String> words = List.of("Hello", " ", "World", "!");
    String sentence = words.stream()
                           .reduce("", String::concat); // Берём пустую строку и прилепляем к ней всё подряд
    System.out.println(sentence); // "Hello World!"

И главное, блядь, запомни: функция-аккумулятор должна быть ассоциативной! То есть (a + b) + c должно равняться a + (b + c). Иначе в параллельных стримах начнётся такой пиздец и рассинхрон, что мало не покажется.