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.
Текст написан своими словами, примеры кода — оригинальные.