引言
reduce是 Java Stream API 中的一个重要方法,用于将流中的元素反复结合起来,得到一个值。它常用于聚合、累加、拼接等操作。下面是详细说明:
1. 基本语法
有三种常用形式:
1.1 无初始值(返回 Optional)
Optional<T> reduce(BinaryOperator<T> accumulator)- 适用于流可能为空的情况,返回
Optional<T>。 accumulator是一个二元函数,接收两个流元素,返回一个合并后的结果。
示例:求最大值
List<Integer> list = Arrays.asList(1, 3, 2, 5, 4); Optional<Integer> max = list.stream().reduce(Integer::max); max.ifPresent(System.out::println); // 输出51.2 有初始值(返回 T)
T reduce(T identity, BinaryOperator<T> accumulator)identity是初始值(如0、""等)。- 返回类型是 T,流为空时直接返回
identity。
示例:求和
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); int sum = list.stream().reduce(0, Integer::sum); System.out.println(sum); // 输出151.3 三参数(并行流用得多)
<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner)- 用于并行流,
accumulator负责每个元素的归约,combiner负责合并各线程的结果。
示例:字符串拼接
List<String> list = Arrays.asList("a", "b", "c"); String result = list.stream().reduce( "", (s1, s2) -> s1 + s2, // accumulator (s1, s2) -> s1 + s2 // combiner ); System.out.println(result); // 输出abc2. 典型用法
求积
int product = Arrays.asList(1, 2, 3, 4).stream().reduce(1, (a, b) -> a * b); System.out.println(product); // 输出24拼接字符串
String concat = Arrays.asList("a", "b", "c").stream().reduce("", (a, b) -> a + b); System.out.println(concat); // 输出abc找最小值
Optional<Integer> min = Arrays.asList(5, 2, 8, 1).stream().reduce(Integer::min); min.ifPresent(System.out::println); // 输出13. 注意事项
- 如果流为空,无初始值的
reduce返回Optional.empty,要注意判空。 - 有初始值时,结果类型与初始值类型一致,流为空时直接返回初始值。
- 对于并行流,三参数形式效率更高。
reduce适合用于聚合操作,简单计数、求和可直接用count、sum、max、min等方法。
4. 高级用法举例
4.1 统计对象属性之和
比如有一个对象列表,统计某个字段的总和:
class Person { String name; int age; // 构造方法和getter略 } List<Person> people = Arrays.asList( new Person("A", 20), new Person("B", 30), new Person("C", 25) ); int totalAge = people.stream() .map(Person::getAge) .reduce(0, Integer::sum); System.out.println(totalAge); // 输出754.2 归约为集合(不推荐,推荐用 collect)
虽然可以用 reduce 拼接集合,但推荐用collect。下面是用 reduce 实现:
List<Integer> list = Arrays.asList(1, 2, 3, 4); List<Integer> result = list.stream().reduce( new ArrayList<Integer>(), (acc, item) -> { acc.add(item); return acc; }, (acc1, acc2) -> { acc1.addAll(acc2); return acc1; } ); System.out.println(result); // 输出[1, 2, 3, 4]注意:这种写法在并行流下可能有线程安全问题,推荐使用collect(Collectors.toList())。
4.3 复杂对象归约
比如合并两个对象:
class Stat { int count; int sum; // 构造、getter、setter略 } List<Stat> stats = Arrays.asList( new Stat(1, 10), new Stat(2, 20), new Stat(3, 30) ); Stat total = stats.stream().reduce( new Stat(0, 0), (s1, s2) -> new Stat(s1.count + s2.count, s1.sum + s2.sum) ); System.out.println(total.count); // 输出6 System.out.println(total.sum); // 输出605. 常见误区
reduce不是万能的聚合工具:
对于集合归约、分组等复杂操作,优先考虑collect,如Collectors.groupingBy、Collectors.toList等。并行流下要注意可变对象:
如果 identity 是可变对象(如 ArrayList),并行流下可能导致线程安全问题。初始值的选择很重要:
初始值应为归约运算的单位元(如加法用0,乘法用1,字符串拼接用"")。
6. 性能建议
- 对于简单聚合(求和、求最大/最小),用
sum()、max()、min()等内置方法更简洁高效。 - 对于复杂归约,优先用
collect,如Collectors.toList()、Collectors.toMap()、Collectors.groupingBy()。
8. 总结一张表
| reduce形式 | 适用场景 | 返回值类型 | 是否可并行 |
|---|---|---|---|
| reduce(BinaryOperator) | 流可能为空 | Optional | 是 |
| reduce(identity, BinaryOperator) | 简单聚合 | T | 是 |
| reduce(identity, accumulator, combiner) | 并行流复杂归约 | U | 是 |
7. 总结
reduce可以实现各种聚合、归约操作。- 三种常用形式:无初始值、有初始值、三参数(并行流)。
- 常见场景:求和、求积、拼接、最大/最小值等。