我试图理解为什么当我在下面的代码片段中从
y.addAll(x)
更改为 x.addAll(y)
时会有不同:
List<Integer> result = List.of(1, 2)
.parallelStream()
.collect(
ArrayList::new,
(x, y) -> x.add(y),
(x, y) -> y.addAll(x)
);
System.out.println(result);
我知道,当我使用
parallelStream
时,一次会运行多个线程。
collect
有三个参数;前两个参数我理解。使用第三个参数,我知道x,y是子流,它们是ArrayList
,但我不明白为什么每种情况下结果都不同。我希望他们是一样的。
(x, y) -> y.addAll(x) // output: [1]
(x, y) -> x.addAll(y) // output: [1, 2]
combiner - 一个关联的、互不干扰的、无状态的函数,它接受两个部分结果容器并将它们合并,它必须与累加器函数兼容。组合器函数必须将第二个结果容器中的元素折叠到第一个结果容器中。类似地,
a.addAll(b)
b
到 a
的元素相加,但反之则不然。它从参数中获取信息并修改接收器。
因此,该方法的约定指定您必须将 lambda 的第二个参数合并到第一个参数中。如果您执行
(x, y) -> x.addAll(y)
y
的所有元素添加到
x
中。但是,对于
(x, y) -> y.addAll(x)
,您将其添加到第二个元素,导致
y
的元素未添加到
x
,然后这些元素在结果中丢失。
发生了什么这样做是因为并行流将处理分成多个块,其中不同的线程处理不同的块。处理之后,它需要将元素合并在一起,这是使用组合器完成的(最后一个 lambda 表达式,就是您谈到的那个)。该组合器需要能够将两个元素“组合”在一起,然后将第一个参数用于进一步处理,而第二个参数则被丢弃。
ArrayList
之后创建一个新的 ArrayList::new
x.addAll(y)
,它将第二个列表添加到第一个列表,然后返回并产生正确的结果。但是,使用
y.addAll(x)
时,它将第一个列表的元素添加到第二个列表中,但 Java 假设您想要第一个列表(因为这就是您应该修改的内容),因此收集返回不包含元素的第一个列表由第二个线程处理。
我已经重命名了一些变量,以更好地说明正在发生的事情。
List<Integer> result = List.of( 1, 2 )
.parallelStream()
.collect(
ArrayList::new,
(thisList,value) -> thisList.add( value ),
(thisList,theOtherList) -> thisList.addAll( theOtherList )
);
System.out.println( result );
Stream::collect
thisList
。
当您将调用的第三个参数更改为
collect()
时,如下所示:
…
(thisList,theOtherList) -> theOtherList.addAll( thisList )
…
您将
thisList
的内容添加到 theOtherList
中,thisList
保持不变,这意味着
theOtherList
的内容未添加到其中。
结果只是
1
纯属巧合。