1. 概述
Java 8是Java语言的一个重要版本,它为开发者们带来了诸多革新。在Java 8中,引入了流(Stream)这个概念,它可以让我们更加方便地处理集合中的数据。在对集合中的数据进行处理时,通常将处理过程分为中间操作和终端操作两个部分。那么,这两个操作的区别是什么呢?
2. 中间操作和终端操作的含义
2.1 中间操作
中间操作,顾名思义,是对数据进行处理的过程中,涉及到的中间步骤。它们并不会立即执行,只有在终端操作调用之后才会执行。中间操作可以看做对原始集合数据的转换处理,转换出一个新的流。中间操作有:
过滤(Filter)
映射(Map)
排序(Sort)
去重(Distinct)
截取(Limit)
跳过(Skip)
2.2 终端操作
终端操作是对数据进行处理的最后一步,它们会触发中间操作进行计算,并且生成处理的结果。终端操作会产生一个新的数据结构或者一个副作用,而且它们只能被使用一次,不能重复使用。终端操作有:
遍历(ForEach)
计数(Count)
查找(Find)
归约(Reduce)
最大值(Max)
最小值(Min)
收集(Collect)
3. 中间操作和终端操作的区别
中间操作和终端操作在Java 8中都是返回一个流(Stream)类型。它们之间最主要的区别在于,当调用终端操作时,整个流才会被处理,并且不可以重新使用。而中间操作只是返回了一个新的流,而并不会对原来的流进行处理。下面通过几个示例来说明这种区别:
3.1 示例1:中间操作和终端操作的顺序
在下面的示例中,我们使用了中间操作map
和终端操作forEach
来对一个字符串进行转换,最后输出转换后的结果。
List list = Arrays.asList("a", "b", "c", "d", "e");
list.stream() // 创建流
.map(s -> s.toUpperCase()) // 转换为大写
.forEach(System.out::println); // 输出结果
可以看到,在这个示例中,我们先使用map
方法对原始流进行了中间操作,然后使用forEach
方法触发计算并输出结果。如果我们把这两个操作的顺序颠倒一下,会出现什么情况呢?
List list = Arrays.asList("a", "b", "c", "d", "e");
list.stream() // 创建流
.forEach(System.out::println) // 输出结果
.map(s -> s.toUpperCase()); // 转换为大写
这个代码片段会报错,提示void cannot be converted to java.util.stream.Stream
。
这是因为,forEach
这个方法是一个终端操作,它返回的是void
类型。如果在它之后再调用中间操作,会抛出一个编译时错误。
3.2 示例2:终端操作的延迟执行
在下面的示例中,我们依次使用了两个终端操作count
和findFirst
来对一个整数流进行计算。
List list = Arrays.asList(1, 2, 3, 4, 5);
long count = list.stream() // 创建流
.filter(i -> i > 2) // 过滤
.count(); // 统计符合要求的个数
System.out.println(count); // 输出结果
Optional opt = list.stream() // 创建流
.filter(i -> i > 2) // 过滤
.findFirst(); // 找到第一个符合要求的元素
System.out.println(opt.get()); // 输出结果
上面的代码中,首先我们使用count()
方法计算符合条件的元素的个数,并将结果赋给count
变量。然后我们又使用findFirst()
方法找到第一个符合条件的元素,并将结果打印出来。
需要注意的是,如果我们在两个终端操作之间插入一个中间操作sorted()
,会发生什么情况呢?
List list = Arrays.asList(1, 2, 3, 4, 5);
Optional opt = list.stream() // 创建流
.filter(i -> i > 2) // 过滤
.sorted() // 排序
.findFirst(); // 找到第一个符合要求的元素
System.out.println(opt.get()); // 输出结果
这段代码会抛出一个NoSuchElementException
异常,表示没有找到符合要求的元素。究竟是为什么呢?这是因为sorted()
方法对中间的数据进行排序,并返回一个新的流,但是该流中并没有符合要求的元素,因此在执行findFirst()
方法时,无法找到符合条件的元素。这就是终端操作对中间操作的延迟执行。
4. 总结
本文介绍了Java 8中中间操作和终端操作的区别。虽然这两个操作都会返回一个流(Stream)类型,但是它们的最主要区别在于,当调用终端操作时,会触发中间操作进行处理,并且不可以重新使用。而中间操作只是返回一个新的流,对原来的流没有任何影响。