java 8 stream reduce详解和误区

简介: java 8 stream reduce详解和误区

目录



java 8 stream reduce详解和误区


简介


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,
                 BinaryOperator<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都是合适的。


本文的例子https://github.com/ddean2009/learn-java-streams/tree/master/stream-reduce

相关文章
|
6月前
|
Java Unix Go
【Java】(8)Stream流、文件File相关操作,IO的含义与运用
Java 为 I/O 提供了强大的而灵活的支持,使其更广泛地应用到文件传输和网络编程中。!但本节讲述最基本的和流与 I/O 相关的功能。我们将通过一个个例子来学习这些功能。
274 1
|
7月前
|
Java API 数据处理
Java新特性:使用Stream API重构你的数据处理
Java新特性:使用Stream API重构你的数据处理
|
7月前
|
Java 大数据 API
Java Stream API:现代集合处理与函数式编程
Java Stream API:现代集合处理与函数式编程
363 100
|
7月前
|
Java API 数据处理
Java Stream API:现代集合处理新方式
Java Stream API:现代集合处理新方式
369 101
|
7月前
|
并行计算 Java 大数据
Java Stream API:现代数据处理之道
Java Stream API:现代数据处理之道
398 101
|
7月前
|
存储 数据可视化 Java
Java Stream API 的强大功能
Java Stream API 是 Java 8 引入的重要特性,它改变了集合数据的处理方式。通过声明式语法,开发者可以更简洁地进行过滤、映射、聚合等操作。Stream API 支持惰性求值和并行处理,提升了代码效率和可读性,是现代 Java 开发不可或缺的工具。
149 0
Java Stream API 的强大功能
|
8月前
|
存储 NoSQL Java
Java Stream API:集合操作与并行处理
Stream API 是 Java 8 提供的集合处理工具,通过声明式编程简化数据操作。它支持链式调用、延迟执行和并行处理,能够高效实现过滤、转换、聚合等操作,提升代码可读性和性能。
|
8月前
|
存储 Java API
Java Stream API:现代数据处理之道
Java Stream API:现代数据处理之道
437 188
|
8月前
|
存储 Java API
Java Stream API:现代数据处理之道
Java Stream API:现代数据处理之道
344 92
|
9月前
|
Oracle Java 关系型数据库
掌握Java Stream API:高效集合处理的利器
掌握Java Stream API:高效集合处理的利器
456 80