转载

Java8 Stream API 详细使用指南

福利

现在关注微信公众号:码农小胖哥, 发送关键字【抽奖】进行抽奖,可有机会获取实体编程书籍。活动只剩下2天了抓紧参与!【本次抽奖截止到本周末】

1. 概述

Java 8 引入的一个重要的特性无疑是 Stream API。Stream 翻译过来是“流”,突然想到的是大数据处理有个流式计算的概念,数据通过管道经过一个个处理器(Handler)进行筛选,聚合,而且流都具有向量性,强调的是对数据的计算处理,而集合强调的是数据集。Stream可以看做是一个可操作的数据集序列,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。有点类似于数据库中的增删改查操作。十分高效而且易于使用。

2. 创建Stream

从不同的数据源创建流有很多方式。 被创建的流并不能改变数据源。什么意思呢?打个比方,从一个集合创建流后,对流的操作并不会改变这个集合的数据状态。我们还是举个例子吧。

Java8 Stream API 详细使用指南

如上图, 我们将一个长度为3的集合放入一个流,过滤掉元素长度不超过4的元素,然后把剩下的迭代出来。同时我们还打印一下原始的数据源 strArr 集合长度看看是否发生了变化。结果虽然从流中移除了 java 这个元素但是 strArr 却并没有变化。

Java8 Stream API 详细使用指南

接下来我们开始梳理一下创建流的方式。

2.1 空流

Java8 Stream API 详细使用指南

你应该注意到上图中我用 Collections 创建了一个空的 List ,流也是一样,而且这两者的意义也是一样的。都是避免为没有元素而返回 null

2.2 从集合创建流

Java8 Stream API 详细使用指南

上面摘自java 8 Collection<E> 说明只要是 Collection<E> 的实现都可以创建流。

Java8 Stream API 详细使用指南

2.3 从数组创建流

我们可以从数组来创建一个流,或者从数组中按照索引截取一部分创建流

Java8 Stream API 详细使用指南

2.4 通过构造器创建流

Stream 提供建造者方法来构建流。不过请注意泛型约束,否则返回的是Object类型的流。

Java8 Stream API 详细使用指南

2.5 无限流

generate() 方法接收 Supplier<T> 函数来生成元素,而且生成如果不加以限制将不会停止,直到内存限制。下面这个例子将生成长度为10,字符串元素长度为5的字符串流

Java8 Stream API 详细使用指南

创建无限流的另一种方法是使用 iterate() 方法。和 generate() 方法一样都要加以限制。不同的是

iterate()方法第一个参数作为起始的种子,第二个函数参数来定制生成元素的规则。下面这个例子

是从1作为第一个元素,每个元素在上一个元素的基础上加1,限制长度为10。下面将打印1-10。

Java8 Stream API 详细使用指南

2.6 基本类型流

Java 8提供了从三种基本类型创建流的可能性:int,long和double。由于Stream <T>是一个通用接口,并且无法使用基本类型作为泛型的类型参数,因此创建了三个新的特殊接口: IntStreamLongStreamDoubleStream

使用这些新的API避免了不必要的自动装箱,从而提高了生产率:

Java8 Stream API 详细使用指南

以上两个方法的起始都是从1开始,步长为1创建序列。区别就是 endExclusive=3
range 方法不包含3,而 rangeClosed 包括3。

从Java 8开始, Random 类为生成基本类型流提供了广泛的方法。例如,以下代码创建一个 DoubleStream ,它有三个随机 double 元素:

Java8 Stream API 详细使用指南

2.7 字符串流

String也可以用作创建流的源。

借助String类的 chars() 方法。由于没有 CharStream ,在JDK的 IntStream 用于表示字符流代替。

Java8 Stream API 详细使用指南

以下示例根据指定的 RegExString 拆分为子字符串:

Java8 Stream API 详细使用指南

2.8 文件流

Java NIO类 Files 允许通过 lines() 方法生成文本文件的 Stream <String> 。文本的每一行都成为流的一个元素:

Java8 Stream API 详细使用指南

你还可以在 lines() 方法中指定字符集编码。

2.9 并行流

并行流就是把一个内容分成多个数据块,并用不同的线程分成多个数据块,并用不同的线程分别处理每个数据块的流。底层用了Fork/Join框架。该流主要用来处理大批量的数据源。少量数据不建议使用。带有parallel的声明方式都是并行流,这里不在介绍。

但是使用一定要注意数据并行处理同步。要么使用同步集合,诸如 Collections.synchronized 系列。或者在并行流收集元素到集合中时,调用 collect 方法,一定不要采用 Foreach 方法或者 map 方法。

3.流的引用

只要只调用中间操作,就可以实例化一个流并具有对它的可访问引用。执行终端操作会使流不可访问。从技术上讲,以下代码看上去是有效的:

Java8 Stream API 详细使用指南

第3行是终端操作 如果接着执行第4行对stream进行重用将触发 IllegalStateException 。一定要谨记 Java 8 中同一个Stream 在终端操作后是不能重用的。 正确的做法是这样的:

Java8 Stream API 详细使用指南

4.流的中间操作

中间操作就是对数据源中的数据的计算操作。其实上面我们已经对流进行很多的中间操作比如 filter()limit() 等等。

网上很著名的一些中间操作讲解

Java8 Stream API 详细使用指南

Java8 Stream API 详细使用指南

Java8 Stream API 详细使用指南

Java8 Stream API 详细使用指南

Java8 Stream API 详细使用指南

Java8 Stream API 详细使用指南

5.流的生命周期

创建Stream 一个数据源(如:集合、数组),获取一个流

中间操作 一个中间操作链,对数据源的数据进行处理

终止操作(终端操作)一个终止操作,执行中间操作链,并产生结果,到此整个流消亡。

6.总结

Java 8 Stream 具有里程碑的意义。改变了以往对数据处理的模式。通过本篇对流以及流的生命周期都做了详尽的说明。相信你已经能够通过Stream来提高你的开发效率。

关注公众号:码农小胖哥 获取更多资讯

原文  https://segmentfault.com/a/1190000020225452
正文到此结束
Loading...