我很难理解为什么并行流和流对于完全相同的语句给出不同的结果。
List<String> list = Arrays.asList("1", "2", "3");
String resultParallel = list.parallelStream().collect(StringBuilder::new,
(response, element) -> response.append(" ").append(element),
(response1, response2) -> response1.append(",").append(response2.toString()))
.toString();
System.out.println("ResultParallel: " + resultParallel);
String result = list.stream().collect(StringBuilder::new,
(response, element) -> response.append(" ").append(element),
(response1, response2) -> response1.append(",").append(response2.toString()))
.toString();
System.out.println("Result: " + result);
ResultParallel:1、2、3
结果:1 2 3
有人可以解释为什么会这样,以及我如何获得非并行版本以提供与并行版本相同的结果吗?
答案 0 :(得分:12)
Java 8 Stream.collect
方法具有以下签名:
<R> R collect(Supplier<R> supplier,
BiConsumer<R, ? super T> accumulator,
BiConsumer<R, R> combiner);
BiConsumer<R, R> combiner
仅在并行流中被调用(以便将部分结果合并到单个容器中),因此第一个代码段的输出为:
ResultParallel: 1, 2, 3
在sequential
版本中,combiner
不会被调用(请参阅此answer),因此以下语句将被忽略:
(response1, response2) -> response1.append(",").append(response2.toString())
结果不同:
1 2 3
如何解决?检查@Eugene的answer或此question and answers。
答案 1 :(得分:8)
要了解原因出了错,请考虑javadoc中的问题。
accumulator
-一种关联,无干扰,无状态的函数,必须将元素折叠到结果容器中。
combiner
-一种关联,无干扰的无状态函数,它接受两个部分结果容器并将其合并,必须与累加器功能兼容。组合器功能必须将元素从第二个结果容器折叠到第一个结果容器中。
这是什么意思,元素是通过“累加”还是“组合”或两者的某种组合来收集都没有关系。但是在您的代码中,累加器和组合器使用不同分隔符进行连接。从Javadoc的要求来看,它们不是“兼容的”。
根据使用的是顺序流还是并行流,导致结果不一致。
在并行情况下,流分为子流 1 ,由不同的线程处理。这导致每个子流的单独收集。然后将这些集合合并。
在顺序情况下,不拆分流。取而代之的是,将流简单地累积到单个集合中,而无需进行合并。
观察:
通常,对于执行简单转换的这种大小的流,parallelStream()
可能会使速度变慢。
在这种特定情况下,parallelStream()
版本的瓶颈将成为合并步骤。这是一个串行步骤,它执行与整个串行管道相同数量的复制。因此,事实上,并行化肯定会使事情变慢。
实际上,lambda行为不正确。它们在开头添加了一个额外的空间,如果使用了combiner
,则将它们增加了一倍。一个更正确的版本是:
String result = list.stream().collect(
StringBuilder::new,
(b, e) -> b.append(b.isEmpty() ? "" : " ").append(e),
(l, r) -> l.append(l.isEmpty() ? "" : " ").append(r)).toString();
Joiner
类是连接流的一种更简单,更有效的方法。 (来源:@Eugene)
1-在这种情况下,每个子流只有一个元素。对于更长的列表,通常将获得与工作线程一样多的子流,并且子流将包含多个元素。
答案 2 :(得分:7)
请注意,即使您将,
中的空格替换为combiner
,您的结果仍然会有所不同(略微更改了代码以使其更具可读性):
String resultParallel = list.parallelStream().collect(
StringBuilder::new,
(builder, elem) -> builder.append(" ").append(elem),
(left, right) -> left.append(" ").append(right)).toString();
String result = list.stream().collect(
StringBuilder::new,
(builder, elem) -> builder.append(" ").append(elem),
(left, right) -> left.append(" ").append(right)).toString();
System.out.println("ResultParallel: ->" + resultParallel + "<-"); // -> 1 2 3 4<-
System.out.println("Result: ->" + result + "<-"); // -> 1 2 3 4<-
注意您的空间太多了。
java-doc具有提示:
组合器...必须与累加器功能兼容
如果您想加入,有一些更简单的选项,例如:
String.join(",", yourList)
yourList.stream().collect(Collectors.joining(","))