JAVA研发狄仁杰 5万字:Stream和Lambda表达式最佳实践2( 四 )


上面的例子中 , 我们HashSet::new作为supplier , Set::add作为accumulator , 自定义了一个方法作为combiner , 最后使用Collections::unmodifiableSet将集合转换成不可变集合 。
上面我们固定使用HashSet::new作为初始集合的生成方法 , 实际上 , 上面的方法可以更加通用:publicstaticCollectortoImmutableSet(Supplier
{left.addAll(right);returnleft;},Collections::unmodifiableSet);}复制代码
上面的方法 , 我们将supplier提出来作为一个参数 , 由外部来定义 。
看下上面两个方法的测试:@TestpublicvoidtoImmutableSetUsage(){Set
stringSet1=Stream.of("a","b","c","d").collect(ImmutableSetCollector.toImmutableSet());log.info("{}",stringSet1);Set
stringSet2=Stream.of("a","b","c","d").collect(ImmutableSetCollector.toImmutableSet(LinkedHashSet::new));log.info("{}",stringSet2);}复制代码
输出:INFOcom.flydean.ImmutableSetCollector-[a,b,c,d]INFOcom.flydean.ImmutableSetCollector-[a,b,c,d]复制代码11.streamreduce详解和误区
StreamAPI提供了一些预定义的reduce操作 , 比如count(),max(),min(),sum()等 。 如果我们需要自己写reduce的逻辑 , 则可以使用reduce方法 。
本文将会详细分析一下reduce方法的使用 , 并给出具体的例子 。 11.1reduce详解
Stream类中有三种reduce , 分别接受1个参数 , 2个参数 , 和3个参数 , 首先来看一个参数的情况:Optionalreduce(BinaryOperatoraccumulator);复制代码
该方法接受一个BinaryOperator参数 , BinaryOperator是一个@FunctionalInterface,需要实现方法:Rapply(Tt,Uu);复制代码
accumulator告诉reduce方法怎么去累计stream中的数据 。
举个例子:List
intList=Arrays.asList(1,2,3);Optional
result1=intList.stream().reduce(Integer::sum);log.info("{}",result1);复制代码
上面的例子输出结果:com.flydean.ReduceUsage-Optional[6]复制代码
一个参数的例子很简单 。 这里不再多说 。
接下来我们再看一下两个参数的例子:Treduce(Tidentity,BinaryOperatoraccumulator);复制代码
这个方法接收两个参数:identity和accumulator 。 多出了一个参数identity 。
也许在有些文章里面有人告诉你identity是reduce的初始化值 , 可以随便指定 , 如下所示:Integerresult2=intList.stream().reduce(100,Integer::sum);log.info("{}",result2);复制代码
上面的例子 , 我们计算的值是106 。
如果我们将stream改成parallelStream:Integerresult3=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的真正意图 。
下面再看一下三个参数的方法:Ureduce(Uidentity,BiFunction
accumulator,BinaryOperatorcombiner);复制代码
和前面的方法不同的是 , 多了一个combiner , 这个combiner用来合并多线程计算的结果 。 同样的 , identity需要满足combiner.apply(u,accumulator.apply(identity,t))==accumulator.apply(u,t)


推荐阅读