问答题809/1053解释Java 8-中间操作与终端操作?

难度:
2021-11-02 创建

参考答案:

在 Java 8 的 Stream API 中,操作可以分为 中间操作(Intermediate Operations)和 终端操作(Terminal Operations)。这两类操作是流式处理的核心组成部分,它们之间的主要区别在于:中间操作是惰性求值的,它们本身不会触发计算,而终端操作则是触发计算并生成最终结果的操作。

1. 中间操作(Intermediate Operations)

中间操作是 惰性操作,意味着它们不会立即执行,而是会返回一个新的流,操作会在最终的终端操作执行时进行实际的处理。中间操作的返回值通常是一个新的 Stream 对象,允许流的链接。

  • 中间操作通常用于对流的数据进行转换、过滤、排序等操作。
  • 多个中间操作可以组合成一个管道(pipeline),每个操作在执行时都不会产生副作用,直到执行终端操作。

常见的中间操作有:

  • filter(Predicate<T> predicate): 用于过滤流中的元素,返回一个只包含符合条件元素的新流。
  • map(Function<T, R> mapper): 用于将流中的元素映射到另一个类型的元素,返回一个新流。
  • flatMap(Function<T, Stream<R>> mapper): 类似于 map,但它的返回值是一个流(Stream),用于将流中的每个元素展开成多个元素。
  • distinct(): 返回去重后的流,去除流中的重复元素。
  • sorted(): 返回按自然顺序排序的流。
  • sorted(Comparator<T> comparator): 返回按指定比较器排序的流。
  • peek(Consumer<T> action): 允许在流操作中进行调试操作(例如打印),它不会修改流中的元素。
  • limit(long maxSize): 返回一个流,其中包含前 maxSize 个元素。
  • skip(long n): 返回一个流,跳过前 n 个元素。

2. 终端操作(Terminal Operations)

终端操作是 非惰性操作,意味着它们会触发流的计算,并且最终会返回一个非流的结果,例如:一个集合、一个数值或一个布尔值等。终端操作结束后,流将会被关闭,不能再被重用。

常见的终端操作有:

  • forEach(Consumer<T> action): 对流中的每个元素执行给定的操作。是一个终端操作,因此会遍历流中的所有元素并执行操作。

    1List<String> list = Arrays.asList("apple", "banana", "cherry"); 2list.stream().forEach(item -> System.out.println(item));
  • collect(Collector<T, A, R> collector): 将流中的元素累积到容器中,通常用于转换为集合(如 ListSet 等),是一个常用的终端操作。

    1List<String> list = Arrays.asList("apple", "banana", "cherry"); 2List<String> result = list.stream().filter(s -> s.startsWith("a")).collect(Collectors.toList());
  • reduce(T identity, BinaryOperator<T> accumulator): 用于将流中的元素反复结合起来,得到一个单一的结果,常用于计算总和、最大值、最小值等。

    1int sum = list.stream().reduce(0, (a, b) -> a + b);
  • count(): 返回流中元素的数量。

    1long count = list.stream().filter(s -> s.startsWith("a")).count();
  • anyMatch(Predicate<T> predicate): 如果流中的任意一个元素满足给定的条件,返回 true

    1boolean result = list.stream().anyMatch(s -> s.startsWith("a"));
  • allMatch(Predicate<T> predicate): 如果流中的所有元素都满足给定的条件,返回 true

    1boolean result = list.stream().allMatch(s -> s.length() > 3);
  • noneMatch(Predicate<T> predicate): 如果流中的没有元素满足给定的条件,返回 true

    1boolean result = list.stream().noneMatch(s -> s.startsWith("z"));
  • findFirst(): 返回流中的第一个元素,返回的是 Optional 类型。

    1Optional<String> first = list.stream().filter(s -> s.startsWith("b")).findFirst();
  • findAny(): 返回流中的任意一个元素,通常用于并行流中,结果不一定是第一个元素。

    1Optional<String> any = list.stream().filter(s -> s.startsWith("b")).findAny();

中间操作与终端操作的关系

  • 惰性求值:中间操作是惰性求值的,意味着它们不会立即执行。只有当终端操作触发时,才会对流进行计算。
  • 管道操作:多个中间操作可以通过方法链的方式连在一起,最终的终端操作才会启动整个计算过程。

例子:流式处理

1List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 2 3// 中间操作:过滤出偶数,映射成平方数,排序 4List<Integer> result = numbers.stream() 5 .filter(n -> n % 2 == 0) // 中间操作:过滤 6 .map(n -> n * n) // 中间操作:映射 7 .sorted() // 中间操作:排序 8 .collect(Collectors.toList()); // 终端操作:收集结果 9 10System.out.println(result); // 输出:[4, 16, 36, 64, 100]

在这个例子中,filtermapsorted 是中间操作,它们的结果会传递到下一个操作;collect 是终端操作,它会触发流的计算并返回最终结果。

最近更新时间:2024-12-12