技术编程5万字:Stream和Lambda表达式最佳实践2( 四 )


【技术编程5万字:Stream和Lambda表达式最佳实践2】toImmutableSet() {return Collector.of(HashSet::new, Set::add,(left, right) -> {left.addAll(right);return left;}, Collections::unmodifiableSet);}复制代码
上面的例子中 , 我们HashSet::new作为supplier , Set::add作为accumulator , 自定义了一个方法作为combiner , 最后使用Collections::unmodifiableSet将集合转换成不可变集合 。
上面我们固定使用HashSet::new作为初始集合的生成方法 , 实际上 , 上面的方法可以更加通用:public static
Collector
toImmutableSet(Supplier
{left.addAll(right);return left;}, Collections::unmodifiableSet);}复制代码
上面的方法 , 我们将supplier提出来作为一个参数 , 由外部来定义 。
看下上面两个方法的测试:@Testpublic void toImmutableSetUsage(){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);}复制代码
输出:INFO com.flydean.ImmutableSetCollector - [a, b, c, d]INFO com.flydean.ImmutableSetCollector - [a, b, c, d]复制代码11. stream reduce详解和误区
Stream API提供了一些预定义的reduce操作 , 比如count(), max(), min(), sum()等 。 如果我们需要自己写reduce的逻辑 , 则可以使用reduce方法 。
本文将会详细分析一下reduce方法的使用 , 并给出具体的例子 。 11.1 reduce详解
Stream类中有三种reduce , 分别接受1个参数 , 2个参数 , 和3个参数 , 首先来看一个参数的情况:Optional reduce(BinaryOperator accumulator);复制代码
该方法接受一个BinaryOperator参数 , BinaryOperator是一个@FunctionalInterface,需要实现方法:R apply(T t, U u);复制代码
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]复制代码
一个参数的例子很简单 。 这里不再多说 。
接下来我们再看一下两个参数的例子:T reduce(T identity, BinaryOperator 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的真正意图 。