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
欢迎关注我的公众号:程序那些事,更多精彩等着您!
更多内容请访问 www.flydean.com
java 8 stream reduce详解和误区的更多相关文章
- Java 8 Stream API详解--转
原文地址:http://blog.csdn.net/chszs/article/details/47038607 Java 8 Stream API详解 一.Stream API介绍 Java8引入了 ...
- Java 8 Stream API 详解
Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利.高效的聚合操作(aggregate operation),或者大批量数据操作 (b ...
- Java8的Stream语法详解(转载)
1. Stream初体验 我们先来看看Java里面是怎么定义Stream的: A sequence of elements supporting sequential and parallel agg ...
- Java8初体验(二)Stream语法详解(转)
本文转自http://ifeve.com/stream/ Java8初体验(二)Stream语法详解 感谢同事[天锦]的投稿.投稿请联系 tengfei@ifeve.com上篇文章Java8初体验(一 ...
- Java8初体验(二)Stream语法详解---符合人的思维模式,数据源--》stream-->干什么事(具体怎么做,就交给Stream)--》聚合
Function.identity()是什么? // 将Stream转换成容器或Map Stream<String> stream = Stream.of("I", & ...
- Storm 第三章 Storm编程案例及Stream Grouping详解
1 功能说明 设计一个topology,来实现对文档里面的单词出现的频率进行统计.整个topology分为三个部分: SentenceSpout:数据源,在已知的英文句子中,随机发送一条句子出去. S ...
- Java 8 Lambda 表达式详解
一.Java 8 Lambda 表达式了解 参考:Java 8 Lambda 表达式 | 菜鸟教程 1.1 介绍: Lambda 表达式,也可称为闭包,是推动 Java 8 发布的最重要新特性. La ...
- java反射机制深入详解
java反射机制深入详解 转自:http://www.cnblogs.com/hxsyl/archive/2013/03/23/2977593.html 一.概念 反射就是把Java的各种成分映射成 ...
- 国际化,java.util.ResourceBundle使用详解
java.util.ResourceBundle使用详解 一.认识国际化资源文件 这个类提供软件国际化的捷径.通过此类,可以使您所编写的程序可以: 轻松地本地化或翻译成不同的 ...
随机推荐
- EOS基础全家桶(六)账号管理
简介 本篇我们会学习最基本的账号相关的操作,包括了创建账号和查询,关于账号资源的操作因为必须先部署系统合约,所以我们会留到后面单独写一篇来讲解. 6-EOS基础全家桶(六)账号管理 简介 账号介绍 账 ...
- SciPy - 正态性 与 KS 检验
假设检验的基本思想 若对总体的某个假设是真实的,那么不利于或者不能支持这一假设的事件A在一次试验中是几乎不可能发生的:如果事件A真的发生了,则有理由怀疑这一假设的真实性,从而拒绝该假设: 假设检验实质 ...
- AbstractQueuedSynchronizer概述
AbstractQueuedSynchronizer 是一个用于在竞争资源(如多线程)时使用的同步器,它内部使用了一个int类型的字段status表示需要同步的资源状态, 并基于一个先进先出(FIFO ...
- IDEA 正式版终于支持中文版和 JDK 直接下载了(太方便了)附介绍视频
IDEA 2020.1 经过了漫长的打磨终于发布正式版了,而这次的版本不止直接支持 Java 14,还带来了两个重量级的功能,官方中文版支持和 JDK 直接下载. 在之前的开发中,当我们需要下载 JD ...
- Spring(二):初始值赋值
依赖注入 1.构造器注入 见spring简介最后一大点. 2.Set方式注入 依赖注入:Set注入 依赖:bean对象的创建依赖于容器. 注入:bean对象的所有属性,由容器注入. bean ,里面属 ...
- 天天写order by,你知道Mysql底层执行原理吗?
前言 文章首发于微信公众号[码猿技术专栏]. 在实际的开发中一定会碰到根据某个字段进行排序后来显示结果的需求,但是你真的理解order by在 Mysql 底层是如何执行的吗? 假设你要查询城市是苏州 ...
- Go gRPC进阶-超时设置(六)
前言 gRPC默认的请求的超时时间是很长的,当你没有设置请求超时时间时,所有在运行的请求都占用大量资源且可能运行很长的时间,导致服务资源损耗过高,使得后来的请求响应过慢,甚至会引起整个进程崩溃. 为了 ...
- Git应用详解第五讲:远程仓库Github与Git图形化界面
前言 前情提要:Git应用详解第四讲:版本回退的三种方式与stash 这一节将会介绍本地仓库与远程仓库的一些简单互动以及几款常用的Git图形化界面,让你更加方便地使用git. 一.Git裸库 简单来说 ...
- Struts2-学习笔记系列(13)-类型转换异常和校验器
Struts2框架有默认的类型转换错误拦截机制,该配置在struts-default.xml中,名叫conversionError,但是想使用需要继承ActionSupport. 默认的错误提示信息是 ...
- 《深入理解 Java 虚拟机》笔记整理
正文 一.Java 内存区域与内存溢出异常 1.运行时数据区域 程序计数器:当前线程所执行的字节码的行号指示器.线程私有. Java 虚拟机栈:Java 方法执行的内存模型.线程私有. 本地方法栈:N ...