如何解决java parallelStream reduce中组合器的实际作用是什么
我对Java流中的reduce
方法有基本的了解。但是,我不清楚parallelStream
中组合器的作用。在下面的代码段中,在第一个块中,我使用了一个组合器,而在第二个块中则没有。但是,在两种情况下结果都是相同的。
List<Integer> intarr = Arrays.asList(10,20,30);
Integer totsum = intarr.stream().reduce(20,(a,b) -> a+b,b) -> a+b);
System.out.println("total sum: "+totsum);
List<Integer> intarr = Arrays.asList(10,b) -> a+b);
System.out.println("total sum: "+totsum);
可以理解,在2参数reduce
方法的情况下,累加器是BinaryOperator
,在3参数reduce
方法的情况下,累加器是BiFunction
。众所周知,这将有助于类型转换。例如,如果我需要将int转换为double,则可以将我的身份指定为20.0,然后合并器将以double类型输出它。
但是,除了类型转换之外,使用组合器还能真正获得什么好处?
解决方法
组合器的目的是处理结果和输入为不同类型的情况,此处不是这种情况。例如,如果您想要一个字符串连接,则可以通过编写
来使用reduce
stream.reduce("",(String str,int i) -> str + i,(String a,String b) -> a + b)
...在这种情况下,当流以 parallel 方式减少时,不同的块将被分别累积,然后与组合器合并。
,3-arg version的签名是:
<U> U reduce(U identity,BiFunction<U,? super T,U> accumulator,BinaryOperator<U> combiner)
适用于以下一项(或两项)为真的情况:
- 结果类型(
U
)与流元素类型(T
)不同。 -
combiner
与accumulator
不同。
如果这些都不是真的,那么2-arg version会更好(更简单,更容易):
T reduce(T identity,BinaryOperator<T> accumulator)
对于问题中显示的示例,使用3-arg版本没有任何优势。
具有不同类型的示例很多,例如查看其他答案。
其中类型相同但combiner
为accumulator
的示例将用于-
负运算符:
List<Integer> intarr = Arrays.asList(10,20,30);
// Sequential processing doesn't use combiner: totsum = -60
Integer totsum = intarr.stream().reduce(0,(a,b) -> a - b,b) -> a - b);
// Parallel processing with same combiner does work: totsum = -20
Integer totsum = intarr.parallelStream().reduce(0,b) -> a - b);
Integer totsum = intarr.parallelStream().reduce(0,b) -> a - b);
// Parallel processing requires a different combiner: totsum = -60
Integer totsum = intarr.parallelStream().reduce(0,b) -> a + b);
这是因为通过并行处理,我们为该输入流获得3个线程,因此代码变为:
thread1Result = accumulator.apply(0/*identity*/,10); // = 0 - 10 = -10
thread2Result = accumulator.apply(0/*identity*/,20); // = 0 - 20 = -20
thread3Result = accumulator.apply(0/*identity*/,30); // = 0 - 30 = -30
// Bad combiner: (a,b) -> a - b
result = combiner.apply(thread1Result,thread2Result); // = -10 - -20 = +10
result = combiner.apply(result,thread3Result); // = +10 - -30 = -20
// Good combiner: (a,b) -> a + b
result = combiner.apply(thread1Result,thread2Result); // = -10 + -20 = -30
result = combiner.apply(result,thread3Result); // = -30 + -30 = -60
,
使用组合器并不是比不使用组合器更“有利”。 reduce
的两个重载用于不同的目的。正如您所确定的,键入转换。
reduce(T,BinaryOperator<T>)
,没有组合器,将返回T
-流管道中相同的事物。另一方面,reduce(U,U>,BinaryOperator<U>)
与组合器一起返回U
-一种与流中的类型不同的类型。
BigInteger sum = bigIntegerStream.reduce(BigInteger.ZERO,BigInteger::add);
我可以使用无组合器的版本来添加BigInteger
的流。流将是Stream<BigInteger>
,而我想要的结果也是BigInteger
类型。而且我可以使用组合器版本将Stream<Long>
与BigInteger
相加。请注意,流的类型与我想要的结果类型有何不同。
BigInteger sum = longStream.reduce(BigInteger.ZERO,(bi,l) -> bi.add(BigInteger.valueOf(l)),BigInteger::add);
可以说,在使用无组合器的map
之前,您还可以做一个reduce
。
当结果类型不同于流的类型时需要组合器的原因是因为reduce
操作可以并行运行。例如,整个流可以分为几个部分,而每个部分并行地减少。
如果结果类型与流类型T
相同,则使用提供的二进制运算符将每个节简化为T
,然后剩下一堆{{1 } s。我们可以使用相同的二元运算符将这堆T
进一步简化为单个T
。
如果结果类型T
与流类型U
不同,那么U
会将每个部分缩减为BiFucntion
,剩下的就是一堆不知道该怎么处理的U
个对象,因为U
仅取BiFunction
和U
并退还T
。我们需要额外的U
来帮助我们结合 BinaryOperator<U>
。
因此,如果您想要不同的结果类型,则必须使用组合器。
此外,U
不是有效的标识值。有效身份必须满足
20
对于所有accumulator.apply(identity,u) == u
,用于无组合器版本,以及
u
对于所有combiner.apply(u,accumulator.apply(identity,t)) == accumulator.apply(u,t)
和u
(对于组合器版本)。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。