上一次【http://www.cnblogs.com/webor2006/p/8297603.html】在最后用stream.iterate()生成了6个奇数,接着基于它来实现如下需求:找出该流中大于2的元素,然后再将每个元素乘以2,然后忽略掉流中的前两个元素,然后再取流中的前两个元素,最后求出流中元素的总和。那具体如何来实现呢?下面一个个条件来实现:

①、找出该流中大于2的元素。

很显然这是一个过滤操作,所以可以使用stream.fitler(),如下:

②、再将每个元素乘以2。

这个当然就是一种元素映射啦,所以可以使用stream.map(),如下:

而它接收的ToIntFunction的接口原型如下:

其具体代码如下:

那有个细节需要思考一下:

其实是为了避免自动装箱、自动拆箱, 为什么?对于map()这个方法要求的Function接口的返回结果必须是Integer类型的,而期望的最终结果应该是int类型的,所以用map()的话则就会自动装箱与自动拆箱,对于它是有一点点性能上的损耗的,可见Java8是极力想办法避勉这种细微的性能问题,因此这些特定类型的方法就应运而生,像mapToInt()中要求的ToIntFunction接口其返回结果就是直接的int类型,所以就不存在自动装箱与自动拆箱的问题,体会一下。

③、然后忽略掉流中的前两个元素。

怎么忽略呢?当然在流中已经提供了现成的方法啦,如下:

④、再取流中的前两个元素。

这时limit()就可以再次发挥作用,如下:

⑤、最后求出流中元素的总和。

直接可以使用stream.sum()方法,如下:

编译运行:

是不是32呢?咱们手动算一下:生成的6个奇数序列为:1, 3, 5, 7, 9, 11,接着从这6个中过滤出大于2的数为:3, 5, 7, 9, 11,然后再对它们进行乘2如下:6,10,14,18,22,然后忽略掉前两个数此时变为:14, 18, 22,再取出前两个元素如下:14, 18,最后求总和为:14 + 18 = 32,嗯~~没问题。

对于这么多的条件采用stream来实现是多么的简便,而且代码的可读性也比较好,试想一下如果采用传统的做法是不是大量的循环临时变量充斥其中,所以说Java8中Lambda表达式、函数式接口、Stream所带来的是突破性的改变。

接下来对于上面的需求再修改一下,将最后的元素求和改为最元素中最小的那个并打印出来 ,那如何搞呢?Stream中也提供有现成的方法,如下:

编译运行:

Optional咱们之前已经学习过了,对于OptionalInt那当然很好理解啦,里面的元素就是int类型,而非是一个泛型了,如下:

另外看一个方法定义的细节:

这是为什么呢?其实是返回int还是OptionalInt根本原因就是其取出来的值有没有可能为空,对于sum()方法而言,如果Stream中的元素为空,那最终直接显示0就好了,如下:

而如果换成min()或max(),这时会得到一个空的Stream,如下:

这时后如果在min()后面再去调用就会出问题了,如下:

所以因为min()返回的是OptionalInt类型,所以得用标准的用法,如下:

如果将过滤条件改成正常滴,如下:

目前stream()已经提供了求最小值、最大值、总和的方法,那如果想一次性将这三个需求全部求解出来,那怎么办呢?当然第一时间能想到的就是调用三次不同的方法不就可以解决了,但是这种方法显示是比较笨重的,其实Java8中还有更好的方法,如下:

其中看一下IntSummaryStatistics类的介绍:

其中针对咱们写的这个流操作的代码再来回顾一下它的分类:

接下来写一段如下代码,会有一些我们意想之外事情发生,如下:

上面由于调用的两个都是中间操作所以都会返回stream,那打印结果是啥呢?下面运行一下:

其重点看一下抛出的异常:流已经被操作了或者已经被关闭了,为什么?其实Java8中的流跟以前io中的流的概念其实是差不多的,意思是流如果被使用过了就不能再次使用了或者说如果流被关闭了也不能再次使用了,而对于咱们报错的这句代码显然流是木有被关闭的,如下:

那证明是该流已经被使用过了,再调用它的distinct()方法则就抛异常了,分析一下是不是这个原因:

那如何解决这个异常了,需对代码进行稍加修改,如下:

但是!!这里不是推荐的写法,推荐就是用链式的方式来写,而不要用临时的变量去接收再继续写,需要注意。

接下来再来探究另外一个问题就是关于stream中的中间操作与终止操作本质上的区别,举一个如下例子:将stream中的元素的首字母变成大写之后再将其输出,如下:

接下来改造一下代码,加入调试打印语句用来观察现象,如下:

编译运行:

如预期没有任何打印,此时如果再加上终止操作forEach(),

这也就是之前一直在说的关于stream包含两种操作:第一种是中间操作,第二种是终止操作,而对于流中所有的中间操作都是lazy的,也就是惰性求值的,如果没有遇到终止操作的时候它是不会执行的,只有当遇到了终止操作之后,而中间的若干个中间操作才会一并执行,这是需要谨记滴。

另上再回过头来看一下之前写的如下代码:

实际上不是咱们表面的想象的那样,要是性能低下Java8也不可能引进流的概念啦,实际上最终只会循环一次,为什么呢?可以这么简单的理解,以于这些中间操作的行为都是存放于Stream中的一个容器当中,一旦遇到终止操作时,则会将这些行为按照咱们写的顺序逐个的应用到集合当中的每一个元素上,所以说性能上不会有任何影响。

接下来继续看下面这个陷阱:

下面运行来论证一下我们的猜测:

这是为什么呢?

如何解决呢?当然就是先限定元素个数再来去重就成了,如下:

所以说对于stream里面的各种方法在实际编写代码时需要注意一下顺序,以勉掉到坑里面去。

java8学习之Stream陷阱剖析的更多相关文章

  1. java8学习之Stream实例剖析

    继续操练Stream,直接上代码: 而咱们要返回ArrayList,显示可以用构造引用来传递到里面,因为它刚好符合Supplier函数式接口的特性:不接收参数返回一个值,所以: 接下来试着将Strea ...

  2. java8学习之Stream分组与分区详解

    Stream应用: 继续举例来操练Stream,对于下面这两个集合: 需求是:将这两个集合组合起来,形成对各自人员打招呼的结果,输出的结果如: "Hi zhangsan".&quo ...

  3. java8学习之Stream介绍与操作方式详解

    关于默认方法[default method]的思考: 在上一次[http://www.cnblogs.com/webor2006/p/8259057.html]中对接口的默认方法进行了学习,那在Jav ...

  4. Java8学习(4)-Stream流

    Stream和Collection的区别是什么 流和集合的区别是什么? 粗略地说, 集合和流之间的差异就在于什么时候进行计算.集合是一个内存中的数据结构,它包含数据结构中目前所有的值--集合中的每个元 ...

  5. java8学习之Predicate深入剖析与函数式编程本质

    上次[http://www.cnblogs.com/webor2006/p/8214596.html]对Predicate函数接口进行了初步的学习,其中提到了在未来要学习的Stream中得到了大量的应 ...

  6. java8学习之Stream源码分析

    上一次已经将Collectors类中的各种系统收集器的源代码进行了完整的学习,而在之前咱们已经花了大量的篇幅对其Stream进行了详细的示例学习,如: 那接下来则通过源代码的角度来对Stream的运作 ...

  7. java8学习之Stream深度解析与源码实践

    继续对流进行学习,首先先说明一下流的特点: 1.Collection提供了新的stream()方法. 2.流不存储,通过管道的方式获取值. 3.本质是函数式的,对流的操作会生成一个结果,不过并不会修改 ...

  8. Java8学习笔记----Lambda表达式 (转)

    Java8学习笔记----Lambda表达式 天锦 2014-03-24 16:43:30 发表于:ATA之家       本文主要记录自己学习Java8的历程,方便大家一起探讨和自己的备忘.因为本人 ...

  9. 这可能是史上最好的 Java8 新特性 Stream 流教程

    本文翻译自 https://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/ 作者: @Winterbe 欢迎关注个人微信公众 ...

随机推荐

  1. OpenID简介

    OpenID 是一个以用户为中心的数字身份识别框架,它具有开放.分散性.OpenID 的创建基于这样一个概念:我们可以通过 URI (又叫 URL 或网站地址)来认证一个网站的唯一身份,同理,我们也可 ...

  2. 配置文件c3p0-config.xml

    <c3p0-config> <!-- 使用默认的配置读取连接池对象 --> <default-config> <!-- 连接参数 --> <pro ...

  3. linux新建文件夹

    mkdir -p .... -p  ----parents no error if existion, make parent directories as needed

  4. 【HANA系列】SAP HANA SQL从给定日期中获取年份

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[HANA系列]SAP HANA SQL从给定日 ...

  5. WEB框架初识

    HTTP介绍 Hyper Text Transfer Protocol,超文本传输书协议,是万维网数据通信的基础,规定了请求和响应标准. HTTP工作原理 HTTP 请求以及响应的步骤 客户端连接到W ...

  6. socket之UDP协议,并发编程介绍,操作系统发展史

    socket之UDP协议 1.UDP协议 UDP 是User Datagram Protocol的简称, 中文名是用户数据报协议,是OSI(Open System Interconnection 参考 ...

  7. Linux批量文件管理

    Linux批量文件管理   实验目标: 通过本实验掌握批量建立.移动.复制文件或目录的操作,也可以作为后续shell编程的基础. 实验步骤: 1.现在有十台终端机器,要为每台机器建立3个文件,总共要建 ...

  8. C# Tcp协议收发数据(TCPClient发,Socket收)

    转载自:http://www.cnblogs.com/WTFly/p/5340617.html 运行这个程序前需要先关闭Windows防火墙,Win7系统关闭防火墙的方法是在控制面板的"控制 ...

  9. 【机器学习】Jackknife,Bootstraping, bagging, boosting, AdaBoosting, Rand forest 和 gradient boosting

    Jackknife,Bootstraping, bagging, boosting, AdaBoosting, Rand forest 和 gradient boosting 这些术语,我经常搞混淆, ...

  10. 谈一谈 Normalize.css

    Normalize.css是一种CSS reset的替代方案.它在默认的HTML元素样式上提供了跨浏览器的高度一致性.相比于传统的CSS reset,Normalize.css是一种现代的.为HTML ...