## 1\. 概述

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

## [](https://www.felord.cn/java8streamapi.html#2-创建Stream "2\.    创建Stream")2\. 创建Stream

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

![file](https://upload-images.jianshu.io/upload_images/19934397-3050512436b345a0.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

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

![file](https://upload-images.jianshu.io/upload_images/19934397-cc2e6bae7579fec1.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

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

### [](https://www.felord.cn/java8streamapi.html#2-1-空流 "2.1 空流")2.1 空流

![file](https://upload-images.jianshu.io/upload_images/19934397-976078e221b8d7d6.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

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

### [](https://www.felord.cn/java8streamapi.html#2-2-从集合创建流 "2.2 从集合创建流")2.2 从集合创建流

![file](https://upload-images.jianshu.io/upload_images/19934397-8e779dbb38845734.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

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

![file](https://upload-images.jianshu.io/upload_images/19934397-fbda7681580d8c3d.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

### [](https://www.felord.cn/java8streamapi.html#2-3-从数组创建流 "2.3 从数组创建流")2.3 从数组创建流

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

![file](https://upload-images.jianshu.io/upload_images/19934397-64f567cfadf470c3.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

### [](https://www.felord.cn/java8streamapi.html#2-4-通过构造器创建流 "2.4 通过构造器创建流")2.4 通过构造器创建流

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

![file](https://upload-images.jianshu.io/upload_images/19934397-bf31be54b4cd47fd.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

### [](https://www.felord.cn/java8streamapi.html#2-5-无限流 "2.5 无限流")2.5 无限流

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

![file](https://upload-images.jianshu.io/upload_images/19934397-68eca8057a8c76fe.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

创建无限流的另一种方法是使用`iterate()` 方法。和`generate()` 方法一样都要加以限制。不同的是
iterate()方法第一个参数作为起始的种子,第二个函数参数来定制生成元素的规则。下面这个例子
是从1作为第一个元素,每个元素在上一个元素的基础上加1,限制长度为10。下面将打印1-10。

![file](https://upload-images.jianshu.io/upload_images/19934397-44ec03e79b62a65b.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

### [](https://www.felord.cn/java8streamapi.html#2-6-基本类型流 "2.6 基本类型流")2.6 基本类型流

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

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

![file](https://upload-images.jianshu.io/upload_images/19934397-1b5246123f364ee3.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

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

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

![file](https://upload-images.jianshu.io/upload_images/19934397-4d80797f5ca512cd.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

### [](https://www.felord.cn/java8streamapi.html#2-7-字符串流 "2.7 字符串流")2.7 字符串流

String也可以用作创建流的源。
借助String类的`chars()`方法。由于没有`CharStream`,在JDK的`IntStream`用于表示字符流代替。

![file](https://upload-images.jianshu.io/upload_images/19934397-dce6bd46ac872765.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

以下示例根据指定的`RegEx`将`String`拆分为子字符串:

![file](https://upload-images.jianshu.io/upload_images/19934397-1daae944c6c8fc45.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

### [](https://www.felord.cn/java8streamapi.html#2-8-文件流 "2.8 文件流")2.8 文件流

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

![file](https://upload-images.jianshu.io/upload_images/19934397-1c6993a31cd88e23.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

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

### [](https://www.felord.cn/java8streamapi.html#2-9-并行流 "2.9 并行流")2.9 并行流

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

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

## [](https://www.felord.cn/java8streamapi.html#3-流的引用 "3.流的引用")3.流的引用

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

![file](https://upload-images.jianshu.io/upload_images/19934397-b905727338673c01.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

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

![file](https://upload-images.jianshu.io/upload_images/19934397-c44858f242439990.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

## [](https://www.felord.cn/java8streamapi.html#4-流的中间操作 "4.流的中间操作")4.流的中间操作

中间操作就是对数据源中的数据的计算操作。其实上面我们已经对流进行很多的中间操作比如`filter()`、`limit()`等等。
网上很著名的一些中间操作讲解

![file](https://upload-images.jianshu.io/upload_images/19934397-4ea10902ed270d11.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

![file](https://upload-images.jianshu.io/upload_images/19934397-e9b82e9fbd5cc1fc.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

![file](https://upload-images.jianshu.io/upload_images/19934397-76fe80eef582422e.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

![file](https://upload-images.jianshu.io/upload_images/19934397-3655be230419775c.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

![file](https://upload-images.jianshu.io/upload_images/19934397-f1ced17e5f080b8a.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

![file](https://upload-images.jianshu.io/upload_images/19934397-4b59a315bbd9c59d.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

## [](https://www.felord.cn/java8streamapi.html#5-流的生命周期 "5.流的生命周期")5.流的生命周期

创建Stream 一个数据源(如:集合、数组),获取一个流
中间操作 一个中间操作链,对数据源的数据进行处理
终止操作(终端操作)一个终止操作,执行中间操作链,并产生结果,到此整个流消亡。

## [](https://www.felord.cn/java8streamapi.html#6-总结 "6.总结")6.总结

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

原创作者:码农小胖哥
转载地址:https://gper.club/articles/7e7e7f7ff7g58gc2g6d

10-30 15:13