1. Stream 流介绍
Stream 不同于其他集合框架,它也不是某种数据结构,也不会保存数据,但是它负责相关计算,使用起来更像一个高级的迭代器。在之前的迭代器中,我们只能先遍历然后在执行业务操作,而现在只需要指定执行什么操作, Stream 就会隐式的遍历然后做出想要的操作。另外 Stream 和迭代器一样的只能单向处理,如同奔腾长江之水一去而不复返。
由于 Stream 流提供了惰性计算和并行处理的能力,在使用并行计算方式时数据会被自动分解成多段然后并行处理,最后将结果汇总。所以 Stream 操作可以让程序运行变得更加高效。
2. Stream 流概念
Stream 流的使用总是按照一定的步骤进行,可以抽象出下面的使用流程。
数据源(source) -> 数据处理/转换(intermedia) -> 结果处理(terminal )
2.1. 数据源
数据源(source)
也就是数据的来源,可以通过多种方式获得 Stream 数据源,下面列举几种常见的获取方式。
- Collection.stream(); 从集合获取流。
- Collection.parallelStream(); 从集合获取并行流。
- Arrays.stream(T array) or Stream.of(); 从数组获取流。
- BufferedReader.lines(); 从输入流中获取流。
- IntStream.of() ; 从静态方法中获取流。
- Stream.generate(); 自己生成流
2.2. 数据处理
数据处理/转换(intermedia)
步骤可以有多个操作,这步也被称为intermedia
(中间操作)。在这个步骤中不管怎样操作,它返回的都是一个新的流对象,原始数据不会发生任何改变,而且这个步骤是惰性计算
处理的,也就是说只调用方法并不会开始处理,只有在真正的开始收集结果时,中间操作才会生效,而且如果遍历没有完成,想要的结果已经获取到了(比如获取第一个值),会停止遍历,然后返回结果。惰性计算
可以显著提高运行效率。
数据处理演示。
@Test
public void streamDemo(){
List<String> nameList = Arrays.asList("Darcy", "Chris", "Linda", "Sid", "Kim", "Jack", "Poul", "Peter");
// 1. 筛选出名字长度为4的
// 2. 名字前面拼接 This is
// 3. 遍历输出
nameList.stream()
.filter(name -> name.length() == 4)
.map(name -> "This is "+name)
.forEach(name -> System.out.println(name));
}
// 输出结果
// This is Jack
// This is Poul复制代码
数据处理/转换
操作自然不止是上面演示的过滤 filter
和 map
映射两种,另外还有 map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered 等。
2.3. 收集结果
结果处理(terminal )
是流处理的最后一步,执行完这一步之后流会被彻底用尽,流也不能继续操作了。也只有到了这个操作的时候,流的数据处理/转换
等中间过程才会开始计算,也就是上面所说的惰性计算
。结果处理
也必定是流操作的最后一步。
常见的结果处理操作有 forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator 等。
下面演示了简单的结果处理的例子。
/**
* 转换成为大写然后收集结果,遍历输出
*/
@Test
public void toUpperCaseDemo() {
List<String> nameList = Arrays.asList("Darcy", "Chris", "Linda", "Sid", "Kim", "Jack", "Poul", "Peter");
List<String> upperCaseNameList = nameList.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
upperCaseNameList.forEach(name -> System.out.println(name + ","));
}
// 输出结果
// DARCY,CHRIS,LINDA,SID,KIM,JACK,POUL,PETER,复制代码
2.4. short-circuiting
有一种 Stream 操作被称作 short-circuiting
,它是指当 Stream 流无限大但是需要返回的 Stream 流是有限的时候,而又希望它能在有限的时间内计算出结果,那么这个操作就被称为short-circuiting
。例如 findFirst
操作。
3. Stream 流使用
Stream 流在使用时候总是借助于 Lambda 表达式进行操作,Stream 流的操作也有很多种方式,下面列举的是常用的 11 种操作。
3.1. Stream 流获取
获取 Stream 的几种方式在上面的 Stream 数据源里已经介绍过了,下面是