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

欢迎关注我的公众号:程序那些事,更多精彩等着您!

更多内容请访问 www.flydean.com

java 8 stream reduce详解和误区的更多相关文章

  1. Java 8 Stream API详解--转

    原文地址:http://blog.csdn.net/chszs/article/details/47038607 Java 8 Stream API详解 一.Stream API介绍 Java8引入了 ...

  2. Java 8 Stream API 详解

    Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利.高效的聚合操作(aggregate operation),或者大批量数据操作 (b ...

  3. Java8的Stream语法详解(转载)

    1. Stream初体验 我们先来看看Java里面是怎么定义Stream的: A sequence of elements supporting sequential and parallel agg ...

  4. Java8初体验(二)Stream语法详解(转)

    本文转自http://ifeve.com/stream/ Java8初体验(二)Stream语法详解 感谢同事[天锦]的投稿.投稿请联系 tengfei@ifeve.com上篇文章Java8初体验(一 ...

  5. Java8初体验(二)Stream语法详解---符合人的思维模式,数据源--》stream-->干什么事(具体怎么做,就交给Stream)--》聚合

    Function.identity()是什么? // 将Stream转换成容器或Map Stream<String> stream = Stream.of("I", & ...

  6. Storm 第三章 Storm编程案例及Stream Grouping详解

    1 功能说明 设计一个topology,来实现对文档里面的单词出现的频率进行统计.整个topology分为三个部分: SentenceSpout:数据源,在已知的英文句子中,随机发送一条句子出去. S ...

  7. Java 8 Lambda 表达式详解

    一.Java 8 Lambda 表达式了解 参考:Java 8 Lambda 表达式 | 菜鸟教程 1.1 介绍: Lambda 表达式,也可称为闭包,是推动 Java 8 发布的最重要新特性. La ...

  8. java反射机制深入详解

    java反射机制深入详解  转自:http://www.cnblogs.com/hxsyl/archive/2013/03/23/2977593.html 一.概念 反射就是把Java的各种成分映射成 ...

  9. 国际化,java.util.ResourceBundle使用详解

    java.util.ResourceBundle使用详解   一.认识国际化资源文件   这个类提供软件国际化的捷径.通过此类,可以使您所编写的程序可以:          轻松地本地化或翻译成不同的 ...

随机推荐

  1. Vertica的这些事(十三)——Vertica备份元数据信息

    ---备份资源池 SELECT 'CREATE RESOURCE POOL ' || name || CASE WHEN memorysize IS NULL THEN ' ' ELSE ' MEMO ...

  2. 052.Kubernetes集群管理-故障排错指南

    一 故障指南 1.1 常见问题排障 为了跟踪和发现在Kubernetes集群中运行的容器应用出现的问题,常用如下查错方法: 查看Kubernetes对象的当前运行时信息,特别是与对象关联的Event事 ...

  3. Jmeter 压力测试笔记(4)--分布式部署

    分布式部署:坑,大坑~ 超级坑~~~~ 在这里坑了2天,整整2天.其它略过不表下面只写经验: 在linux下,centos7系统   1主 14执行机. jmeter版本 5.2.1  所有机器在同一 ...

  4. IO 模型知多少

    1. 引言 同步异步I/O,阻塞非阻塞I/O是程序员老生常谈的话题了,也是自己一直以来懵懵懂懂的一个话题.比如:何为同步异步?何为阻塞与非阻塞?二者的区别在哪里?阻塞在何处?为什么会有多种IO模型,分 ...

  5. 程序员的娱乐项目:Arduino 之 HelloWorld

    文章更新于:2020-03-14 文章目录 一.什么是Arduino 二.怎么购买Arduino 三.Arduino都是有那些版本 四.哪里可以找到Arduino的资料 五.Arduino 的供电电源 ...

  6. (js描述的)数据结构[栈结构](2)

    (js描述的)数据结构[栈结构](2) 一.什么是栈结构 1.一种受限制的线性结构,这种结构可以基于数组来实现. 2.可以抽象成一个容器,上面的是栈顶,底下的是栈底.所以仅允许对栈顶进行操作, 二.栈 ...

  7. lr使用soap协议,来对webservice接口进行测试

    实际项目中基于WSDL来测试WebService的情况并不多,WSDL并不是WebService测试的最佳选择. 最主要的原因还是因为WSDL文档过于复杂. 在案例(天气预报WebService服务) ...

  8. search(4)- elastic4s-ElasticDsl

    上次分析了一下elastic4s的运算框架.本来计划接着开始实质的函数调用示范,不过看过了Elastic4s的所有使用说明文档后感觉还是走的快了一点.主要原因是elasticsearch在7.0后有了 ...

  9. coding++:都说新的Arraylist 扩容是(1.5倍+1) 看了1.8的源代码发现不是这么回事

     都说新的Arraylist 扩容是(1.5倍+1) 看了1.8的源代码发现不是这么回事 就用下面这段代码在jdk的三个版本运行看了下效果: import java.lang.reflect.Fiel ...

  10. 创建 SysV 风格的 linux daemon 程序

    本文介绍如何使用 C 语言创建 Linux 系统中 SysV 风格的 daemon 程序.注意:这是一种旧式的 daemon 程序写法,进入 systemd 时代后是不需要通过这样的方式创建 daem ...