为什么以可变对象为身份在并行流中调用 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);
有如下看似"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);