Stream reduce

reduce — терминальная операция Stream API, которая «сворачивает» все элементы потока в одно значение. Это обобщение операций sum, min, max, count — те фактически частные случаи свёртки. Где sum уже не подходит (например, конкатенация строк или своя бизнес-логика), на помощь приходит reduce.

Свёртка задаётся бинарной операцией: «как из двух элементов получить один». Эту операцию Java последовательно применяет ко всему потоку.

Три перегрузки

Optional<T> reduce(BinaryOperator<T> op)
T           reduce(T identity, BinaryOperator<T> op)
<U> U       reduce(U identity,
                   BiFunction<U, ? super T, U> accumulator,
                   BinaryOperator<U> combiner)

identity — нейтральный элемент (0 для сложения, 1 для умножения, "" для конкатенации). Третья форма нужна для параллельных потоков, когда тип результата отличается от типа элементов.

Примеры

Пример 1. Сумма через reduce

import java.util.Arrays;
import java.util.List;

public class ReduceSum {
    public static void main(String[] args) {
        List<Integer> values = Arrays.asList(3, 5, 7, 9);
        int sum = values.stream().reduce(0, Integer::sum);
        System.out.println("Сумма: " + sum);
    }
}

Output:

Сумма: 24

Пример 2. Максимум без identity

import java.util.Arrays;
import java.util.List;
import java.util.Optional;

public class ReduceMax {
    public static void main(String[] args) {
        List<Integer> distances = Arrays.asList(12, 8, 25, 3, 40);
        Optional<Integer> max = distances.stream().reduce(Integer::max);
        max.ifPresent(v -> System.out.println("Максимум: " + v));

        Optional<Integer> emptyMax = Arrays.<Integer>asList().stream().reduce(Integer::max);
        System.out.println("Пустой максимум присутствует: " + emptyMax.isPresent());
    }
}

Output:

Максимум: 40
Пустой максимум присутствует: false

Пример 3. Произведение

import java.util.Arrays;
import java.util.List;

public class ReduceProduct {
    public static void main(String[] args) {
        List<Integer> factors = Arrays.asList(1, 2, 3, 4, 5);
        int product = factors.stream().reduce(1, (a, b) -> a * b);
        System.out.println("5! = " + product);
    }
}

Output:

5! = 120

Пример 4. Конкатенация строк

import java.util.Arrays;
import java.util.List;

public class ReduceConcat {
    public static void main(String[] args) {
        List<String> parts = Arrays.asList("Робот", "Phobo", "v1");
        String name = parts.stream().reduce("", (a, b) -> a.isEmpty() ? b : a + "-" + b);
        System.out.println(name);
    }
}

Output:

Робот-Phobo-v1

Пример 5. Свёртка в другой тип (3 аргумента)

import java.util.Arrays;
import java.util.List;

public class ReduceTriple {
    public static void main(String[] args) {
        List<String> words = Arrays.asList("ик", "линия", "уз", "сервопривод");
        int totalLength = words.stream()
                               .reduce(0,
                                       (acc, s) -> acc + s.length(),
                                       Integer::sum);
        System.out.println("Сумма длин: " + totalLength);
    }
}

Output:

Сумма длин: 20

Пример 6. Среднее измерений

import java.util.Arrays;
import java.util.List;

public class ReduceAverage {
    public static void main(String[] args) {
        List<Integer> samples = Arrays.asList(10, 12, 11, 13, 9);
        int sum = samples.stream().reduce(0, Integer::sum);
        double avg = (double) sum / samples.size();
        System.out.println("Среднее расстояние: " + avg + " см");
    }
}

Output:

Среднее расстояние: 11.0 см

Подводные камни

Предупреждение

  • identity должен быть настоящим нейтральным элементом: для любого x выполняется op(identity, x) == x. Иначе при параллельном выполнении результат «поплывёт».

  • Перегрузка без identity возвращает Optional — поток может быть пуст.

  • Операция должна быть ассоциативной ((a op b) op c == a op (b op c)), иначе parallelStream сломается.

  • Для подсчёта суммы/среднего на примитивах быстрее и удобнее mapToInt(...).sum() / .average().

  • Не используйте reduce для накопления в коллекцию — для этого есть collect.

См. также

Примечание

Материал подготовлен по мотивам документации пакета java.util.stream и распространяется в рамках Oracle Free Documentation License. Текст написан своими словами, примеры кода — оригинальные.