一、集合流的概述
1.1 集合的流式编程简介
Stream 是 JDK1.8 之后出现的新特性,也是 JDK1.8 新特性中最值得学习的两种新特性之一。(另外一个是 Lambda 表达式)
Stream 是对集合操作的增强,流不是集合的元素,不是一种数据结构,不负责数据的存储。流更像是一个迭代器,可以单向的遍历一个集合中的每个元素,并且不可循环。
1.2 集合的流式编程的优点
为什么要用集合的流式编程呢?想必它一定有值得人们去关注的优点。
有些时候,对集合的元素进行操作的时候,需要使用到其他操作的结果。在这个过程中,集合的流式编程可以大幅度的简化代码的数量。将数据源中的数据读取到一个流中,可以对这个流中的数据进行操作(删除、过滤、映射…)。每次的操作结果也是一个流对象,可以对这个流再进行其他的操作。
1.3 使用流式编程的步骤
通常情况下,对集合中的数据使用流式编程,需要经过以下三步:
- 获取数据源,将数据源中的数据读取到流中;
- 对流中的数据进行各种各样的处理;
- 对流中的数据进行整合处理。
在上述三个过程中:对于过程 2,有若干方法可以对流中的数据进行各种各样的操作,并且返回流对象本身,这样的操作,被称为中间操作。对于过程 3,有若干方法可以对流中的数据进行各种处理,并且关闭流,这样的操作,被称为最终操作。
在中间操作和最终操作中,基本上所有的方法参数都是函数式接口,可以使用 Lambda 表达式来实现。使用集合的流式编程来简化代码量,需要对 Lambda 表达式做到熟练掌握。
二、数据源的获取
2.1 数据源简介
数据源,顾名思义,就是流中的数据的来源。读取数据源是流式编程的第一步。注意:数据被读取到流中之后,流中进行处理的数据与数据源中的数据没有关系。也就是说,中间操作对流中的数据进行处理、过滤、映射、排序等操作是不会影响到数据源中的数据的。
2.2 数据源的获取
这个过程,实际上是将一个容器中的数据读取到另一个流中。因此无论什么容器作为数据源,读取到流中的方法的返回值一定是一个 Stream。
如下所示为数据源的获取的示例代码:
/**
* @Description 将集合作为数据源,读取集合中的数据到一个流中
*/
private static void collectionDataSource(){
// 1. 实例化一个集合
ArrayList<Integer> list = new ArrayList<>();
// 2. 填充元素到集合中
Collections.addAll(list, 1, 2, 3, 4, 5);
// 3. 读取集合中的元素到流中
Stream<Integer> stream = list.stream();
System.out.println(stream);
}
/**
* @Description 将数组作为数据源,读取数组中的数据到一个流中
*/
private static void arrayDataSource(){
// 1. 实例化一个数组
Integer[] arr = new Integer[]{
1, 2, 3, 4, 5};
// 2. 读取数组中的数据到流中
Stream<Integer> stream = Arrays.stream(arr);
System.out.println(stream);
}
三、最终操作
3.1 最终操作简介
最终操作就是将流中的数据整合到一起,比如可以存入到一个集合中,也可以直接对流中的数据进行遍历、统计等。通过最终操作,可以从流中提取出来我们想要的信息。
Stream
流的最终操作主要有以下几种:
【注意事项】
之所以叫最终操作,是因为在最终操作结束之后,会关闭这个流,流中所有的数据都会被销毁。如果使用一个已经关闭了的流,会出现异常。
3.2 collect
collect()
方法的作用就是将流中的数据收集到一起,最常见的处理就是将流中的数据存入一个集合中。collect
方法的参数是一个 Collector 接口,而且这个接口并不是一个函数式接口,因此我们可以自定义类来实现这个接口。但是在绝大多数情况下,是不需要我们自定义的,我们借助工具类 Collectors
即可。
【举例说明】
-
例一:将 List 集合中的数据读取到流中,然后原封不动将流中数据存入 Set 集合中。
public class StreamDemo_02 { public static void main(String[] args) { Stream<Integer> stream = getDataSource(); Set<Integer> set = stream.collect(Collectors.toSet()); // 将 stream 中数据存入 set 集合中 System.out.println(set); } /** * 获取数据源 */ private static Stream<Integer> getDataSource(){ Integer[] list = new Integer[]{ 1, 2, 3, 4, 5}; return Arrays.stream(list); } }
运行结果如下:
-
例二:将 List 集合中的数据读取到流中,然后原封不动将流中数据存入 Map 集合中。
public class StreamDemo_02 { public static void main(String[] args) { Stream<Integer> stream = getDataSource(); /** * 将数据存入 map 中,这里用到了 lambda 表达式 * 这里的 Collectors.toMap 的函数原型是: * Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper, * Function<? super T, ? extends U> valueMapper) * 通过函数原型可以看到,函数的参数都是函数式接口 Function。 * 其中:参数一为 key 的映射,参数二为 value 的映射。 * 因此,可以使用 lambda 表达式,将 key 映射为集合元素的字符串形式,将 value 映射为集合元素本身 */ Map<String, Integer> map = stream.collect(Collectors.toMap(x -> x.toString(), x -> x)); // 将stream中数据存入map中 System.out.println(map); } /** * 获取数据源 */ private static Stream<Integer> getDataSource(){ Integer[] list = new Integer[]{ 1, 2, 3, 4, 5}; return Arrays