java8streamreduce参数介绍和详解
⽂章⽬录
简介
Stream API提供了⼀些预定义的reduce操作,⽐如count(), max(), min(), sum()等。如果我们需要⾃⼰写reduce的逻辑,则可以使⽤reduce⽅法。
本⽂将会详细分析⼀下reduce⽅法的使⽤,并给出具体的例⼦。
reduce详解
Stream类中有三种reduce,分别接受1个参数,2个参数,和3个参数,⾸先来看⼀个参数的情况:
⼀个参数
Optional<T> reduce(BinaryOperator<T> accumulator);
该⽅法接受⼀个BinaryOperator参数,BinaryOperator是⼀个@FunctionalInterface,需要实现⽅法:
R apply(T t, U u);
accumulator告诉reduce⽅法怎么去累计stream中的数据。
举个例⼦:
List<Integer> intList = Arrays.asList(1,2,3);
Optional<Integer> result1=intList.stream().reduce(Integer::sum);
log.info("{}",result1);
输出结果
com.flydean.ReduceUsage - Optional[6]
⼀个参数的例⼦很简单。这⾥不再多说。
两个参数
接下来我们再看⼀下两个参数的例⼦:
T reduce(T identity, BinaryOperator<T> accumulator);
这个⽅法接收两个参数:identity和accumulator。多出了⼀个参数identity。
也许在有些⽂章⾥⾯有⼈告诉你identity是reduce的初始化值,可以随便指定,如下所⽰:
Integer result2=intList.stream().reduce(100, Integer::sum);
log.info("{}",result2);
上⾯的例⼦,我们计算的值是106。
如果我们将stream改成parallelStream:
Integer result3=intList.parallelStream().reduce(100, Integer::sum);
log.info("{}",result3);
得出的结果就是306。
为什么是306呢?因为在并⾏计算的时候,每个线程的初始累加值都是100,最后3个线程加出来的结果就是306。
并⾏计算和⾮并⾏计算的结果居然不⼀样,这肯定不是JDK的问题,我们再看⼀下JDK中对identity的说明:
identity必须是accumulator函数的⼀个identity,也就是说必须满⾜:对于所有的t,都必须满⾜ accumulator.apply(identity, t) == t
所以这⾥我们传⼊100是不对的,因为sum(100+1)!= 1。
这⾥sum⽅法的identity只能是0。
如果我们⽤0作为identity,则stream和parallelStream计算出的结果是⼀样的。这就是identity的真正意图。
三个参数
下⾯再看⼀下三个参数的⽅法:
<U> U reduce(U identity,
BiFunction<U, ? super T, U> accumulator,
java streamBinaryOperator<U> combiner);
和前⾯的⽅法不同的是,多了⼀个combiner,这个combiner⽤来合并多线程计算的结果。
同样的,identity需要满⾜combiner.apply(u, accumulator.apply(identity, t)) == accumulator.apply(u, t)
⼤家可能注意到了为什么accumulator的类型是BiFunction⽽combiner的类型是BinaryOperator?
public interface BinaryOperator<T> extends BiFunction<T,T,T>
BinaryOperator是BiFunction的⼦接⼝。BiFunction中定义了要实现的apply⽅法。
其实reduce底层⽅法的实现只⽤到了apply⽅法,并没有⽤到接⼝中其他的⽅法,所以我猜测这⾥的不同只是为了简单的区分。
总结
虽然reduce是⼀个很常⽤的⽅法,但是⼤家⼀定要遵循identity的规范,并不是所有的identity都是合适的。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论