为什么以可变对象为身份在并行流中调用 reduce 方法不保留结果中的顺序?

Why calling the reduce method in the parallel stream with a mutable object as identity does not reserve the order in the result?

有如下看似"correct"的代码:

List<String> list = Arrays.asList("1","2","3","4","5","6",
    "7","8","9","10","11","12");
String result = list.parallelStream()
   .reduce(new StringBuilder(), StringBuilder::append, 
       StringBuilder::append).toString();
System.out.println(result);

这个片段的问题在于identity,new StringBuilder(),在reduce方法调用中是mutable,因此 result 破坏了 ,即 result 的顺序不保留。但是我无法完全理解原因,因此我无法想象以与原始 list 不同的顺序生成 result 的情况。于是画了对应的map-reduce图,偶然得到了result保序:

问:首先,我想确认这张图是正确的。其次,如果这个图是正确的,我想知道这个代码片段不总是产生 result 保留顺序

的原因在哪里

您的图表不正确 - 您假设每个并行缩减都以新的 StringBuilder 开始。取而代之的是,每个并行归约开始时 使用相同的标识元素 - 使用相同的 StringBuilder (您创建并作为第一个参数传递给 reduce 方法的那个)。

每个并行流在您传递给 reduce 方法的(唯一的)StringBuilder 上调用 StringBuilder.append,从而将当前遇到的元素附加到它。

下一步是合并部分结果,方法是在同一个 StringBuilder 上调用 StringBuilder.append,将 StringBuilder 内容的副本附加到自身。


要创建您绘制的图表,您必须将 Supplier<StringBuilder> 作为第一个参数传递给 reduce 操作。

实际上,根据 Holger 的评论,这在使用 Mutable Reduction 时是可能的。

为此,您不调用 reduce 方法,而是调用 collect 方法:

List<String> list = Arrays.asList("1","2","3","4","5","6",
    "7","8","9","10","11","12");
String result = list.parallelStream()
   .collect(StringBuilder::new, StringBuilder::append, 
       StringBuilder::append).toString();
System.out.println(result);