专栏首页Java技术栈跟我学 Java 8 新特性之 Stream 流(三)缩减操作

跟我学 Java 8 新特性之 Stream 流(三)缩减操作

投稿作者:揭光智 | 简书

和前面两篇文章一起服用,效果会更佳。通过对流API的基础体验Demo和关键知识点的讲解,相信大家对流API都有一定的认识了,但是流API强大的功能,可不仅仅像前面两篇文章中说的那样简单,大家应该注意到,在第二篇中,我对Stream接口进行介绍的时候,并没有把他的全部方法都进行了解析说明。

没错,从这一篇开始,那些还没有讲解的方法,很可能就开始变成我们的主角了,大家从题目上面应该知道了,本期我们要讲的是流API的缩减操作。

何为缩减操作?

我们先考虑一下min()和max(),这两个方法我们在第一篇和第二篇中均有提到,其中min()是返回流中的最小值,而max()返回流中最大值,前提是他们存在。

他们之间的特点是什么?

①都返回了一个值

②由一可知,他们是终端操作。

如果我们用流API的术语来形容前面这两种特性的结合体的话,它们代表了缩减操作。因为每个缩减操作都把一个流缩减为一个值,好比最大值,最小值。当然流API,把min()和max(),count()这些操作称为特例缩减。

即然说到了特例,肯定就有泛化这种概念了,他就是reduce()方法了,其实第二篇当中,他已经出现过了,只是当时我没有去强调他。

public interface Stream<T> extends BaseStream<T, Stream<T>> {//、、、忽略其他无关紧要的元素T reduce(T identity, BinaryOperator<T> accumulator);Optional<T> reduce(BinaryOperator<T> accumulator);<U> U reduce(U identity,          BiFunction<U, ? super T, U> accumulator,          BinaryOperator<U> combiner);}

Stream接口定义了三个版本的reduce(),我们先使用前面两个,

T reduce(T identity, BinaryOperator<T> accumulator);//1Optional<T> reduce(BinaryOperator<T> accumulator);//2

第一个版本返回的是一个T类型的对象,T代表的是流中的元素类型!第二个版本是返回一个Optional类型对象。对于这两种形式,accumulator是一个操作两个值并得到结果的函数。在第一个版本当中,identity是这样一个值,对于涉及identity和流中任意的累积操作,得到的结果就是元素自身,没有任何改变。比如,如果是加法,他就是0,如果是乘法他就是1。

其中的accumulator是一个BinaryOperator的类型,他是java.util.function包中声明的函数式接口,它扩展了BiFunction函数式接口.

@FunctionalInterfacepublic interface BinaryOperator<T> extends BiFunction<T,T,T> {}@FunctionalInterfacepublic interface BiFunction<T, U, R> {   R apply(T t, U u);//notice}

BiFunction接口中的apply()方法的原型在 //notice。其中R指定了结果的类型,T,U分别是第一参数的类型和第二个参数的类型,因此apply()对他的两个操作数(t,u)应用到同一个函数上,并返回结果,而对BinaryOperator来说,他在扩展 BiFunction时,指定了所有的类型参数都是相同的T,因此对于BinaryOperator函数式接口的apply来说,他也就变成了 T apply(T t, T u),此外,还有一个需要注意的地方是,在应用reduce()时,apply()的第一个参数t,包含的是一个结果,u包含的是下一个元素。在第一次调用时,将取决于使用reduce()的版本,t可能是单位值,或者是前一个元素。

缩减操作的三个约束

  • 无状态
  • 不干预
  • 关联性

无状态,这里可不是LOL的那个无状态,毕竟他退役了。相信读过第二篇文章的同学已经很容易理解了,简单来说无状态就是每个元素都被单独地处理,他和流中的其它元素是没有任何依赖关系的。不干预是指操作数不会改变数据源。最后,操作必须具有关联性,这里的关联性是指标准的数学含义,即,给定一个关联运算符,在一系列操作中使用该运算符,先处理哪一对操作数是无关紧要的。比如,(1 * 2) * 3 <===> 1 * (2 * 3)。

其中关联性,在并行流中,是至关重要的。下面我用一个简单的例子带着大家实战一下泛化缩减操作reduce()的使用。

public class Main {    public static void main(String[] args) {        learnStream();    }    private static void learnStream() {        List<Integer> lists = new ArrayList<>();        lists.add(1);        lists.add(2);        lists.add(3);        lists.add(4);        lists.add(5);        lists.add(6);        Optional<Integer> sum = lists.stream().reduce((a, b) -> a + b);        if (sum.isPresent()) System.out.println("list的总和为:" + sum.get());//21        //<====> lists.stream().reduce((a, b) -> a + b).ifPresent(System.out::println);        Integer sum2 = lists.stream().reduce(0, (a, b) -> a + b);//21        System.out.println("list的总和为:" + sum2);        Optional<Integer> product = lists.stream().reduce((a, b) -> a * b);        if (product.isPresent()) System.out.println("list的积为:" + product.get());//720        Integer product2 = lists.stream().reduce(1, (a, b) -> a * b);        System.out.println("list的积为:" + product2);//720    }}

这个Demo主要是计算了一个list里面的总和,积的操作,大家可以和传统的算总和,积的方法进行对照,比一比衡量一下就有自己的答案了。但是如果你以为流API仅此而已,那你就错了。

越是后面的东西,就越装B,我在刚知道他们的时候,反正是被吓了一跳的,但这些都是后话了,现在我们来详解一下Demo,并给出扩展的方向:我们这个例子主要是用了lambda表达式对list进行了求和,求积,对于第一个版本为说,求和的时候,identity的值为0,求积的时候它的值为1,强烈建议你们自己感受一下identity的变化对整个结果的变化产生什么 的影响,改变一下identity的值,再运行一下,你就有结果了,另一个扩展点是:

 Integer product3 = lists.stream().reduce(1, (a, b) -> {    if (b % 2 == 0) return a * b; else return a;//这里你可以为所欲为! }); System.out.println("list的偶数的积为:" + product3);//48

本文分享自微信公众号 - Java技术栈(javastack)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-08-01

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 跟我学 Java 8 新特性之 Stream 流(四)并行流

    在开始讨论并行流之前,我先引发一下大家的思考,就你看到这篇文章的时间,你们是不是经常听到,Intel i7 CPU什么8核16线程,什么Android手机8核4...

    Java技术栈
  • Hashtable 为什么不叫 HashTable?

    前几天在写《HashMap 和 Hashtable 的 6 个区别》这篇文章的时候,差点把 Hashtable 写成了 HashTable,后来看源码证实了是:...

    Java技术栈
  • 美团技术四面经历,作者已拿到Offer!

    blog.csdn.net/csuliyajin2012/article/details/49430659

    Java技术栈
  • 第12篇-Elasticsearch全文查询

    另外Elasticsearch入门,我强烈推荐ElasticSearch新手搭建手册和这篇优秀的REST API设计指南 给你,这两个指南都是非常想尽的入门手册...

    普通人刘大
  • 送给你一份《生信入门指南》

    生信的作用越来越大,想学的人越来越多,不管是为了以后发展,还是为了解决眼下的问题。但生信学习不是一朝一夕就可以完成的事情,也许你可以很短时间学会一个交互式软件的...

    科研菌
  • 要测试却没有测试机?那都不是事儿!

    ? “老王,把你那台三星I-9500测试机借我一下” “不行,我自己也要用的。” “**,那明天要交报告了我去哪找测试机?” “那就去WeTest云真机约一个...

    WeTest质量开放平台团队
  • Python数据类型详解-列表#学习猿地

    + 在定义列表中的元素时,需要在每个元素之间使用逗号,进行分隔。[1,2,3,4]

    学习猿地
  • Python数据类型详解-列表#学习猿地

    + 在定义列表中的元素时,需要在每个元素之间使用逗号,进行分隔。[1,2,3,4]

    学习猿地
  • 有道云笔记里几种我觉得比较有用的操作

    Jerry Wang
  • 智能哲学:关于AlphaGo Thesis和思维的范式转移

    ? 非常感谢人民大学刘老师的邀请,也很高兴有这个机会与大家交流学习。本来谈哲学我是极其担心的,但一想到今天是愚人节,可以随便讲,就答应了刘老师。另一个原因,就...

    新智元

扫码关注云+社区

领取腾讯云代金券