为何Java 8 Stream的reduce方法不同合并函数返回结果一致?
为什么不同的reduce合并函数返回相同结果?
先看你给出的Java代码:
public static void main(String... aa) { Stream<Integer> stream = Arrays.asList(1, 2, 3, 4, 5, 6).stream(); List<Integer> numbers = stream.reduce( new ArrayList<Integer>(), (List<Integer> l, Integer e) -> { l.add(e); return l; }, (List<Integer> l1, List<Integer> l2) -> { l1.addAll(l2); return l1; }); System.out.println("numbers" + numbers); stream = Arrays.asList(1, 2, 3, 4, 5, 6).stream(); numbers = stream.reduce(new ArrayList<Integer>(), (List<Integer> l, Integer e) -> { l.add(e); return l; }, (List<Integer> l1, List<Integer> l2) -> { return l1; }); System.out.println("numbers" + numbers); stream = Arrays.asList(1, 2, 3, 4, 5, 6).stream(); numbers = stream.reduce(new ArrayList<Integer>(), (List<Integer> l, Integer e) -> { l.add(e); return l; }, (List<Integer> l1, List<Integer> l2) -> { return null; }); System.out.println("numbers" + numbers); }
执行后输出结果均为:
numbers[1, 2, 3, 4, 5, 6] numbers[1, 2, 3, 4, 5, 6] numbers[1, 2, 3, 4, 5, 6]
核心原因:串行流不会触发合并函数
你这里用的是串行流(stream()),在串行流的执行逻辑里,reduce的合并函数(第三个参数)完全不会被调用。
串行流的处理流程是:从初始的ArrayList开始,逐个把元素通过累加器(第二个参数)添加到同一个列表里,全程不需要拆分任务,自然也就不需要合并多个中间结果。所以不管你把合并函数写成什么逻辑,都不会影响最终结果——因为它根本没被执行过。
并行流才会用到合并函数
如果把代码里的stream()改成parallelStream()(并行流),情况就完全不同了:
- 第一个例子的合并函数正常合并两个列表,结果还是完整的
[1,2,3,4,5,6](元素顺序可能因为并行处理发生变化)。 - 第二个例子的合并函数只返回第一个列表,会丢失第二个子任务处理的元素,结果可能是
[1,2,3]或其他不完整的列表(具体取决于任务拆分方式)。 - 第三个例子的合并函数返回
null,在合并阶段会直接抛出NullPointerException,因为无法用null完成结果合并。
额外提醒:你的累加器写法有问题
顺便提一句,你这里的累加器直接修改传入的初始列表,不符合reduce的设计规范——reduce的累加器应该是无副作用的,即不修改输入参数,而是返回新的对象。如果在并行流里这么写,会因为多个线程同时修改同一个列表导致并发问题。正确的写法应该是每次返回新列表:
(List<Integer> l, Integer e) -> { List<Integer> newList = new ArrayList<>(l); newList.add(e); return newList; }
内容的提问来源于stack exchange,提问作者martinnovoty
相关产品推荐
相关产品推荐

