Java 惰性流

Java 惰性流,用户希望处理满足条件所需的最小数量的流元素。流是惰性的,在达到终止条件前不会处理元素,达到终止条件后逐个处理每个元素。如果遇到短路操作,那么只要满足所有条件,流处理就会终止。读者在第一次接触流处理时,很容易认为流处理的效率不高。

Java 惰性流 问题描述

用户希望处理满足条件所需的最小数量的流元素。

Java 惰性流 解决方案

流是惰性的,在达到终止条件前不会处理元素,达到终止条件后逐个处理每个元素。如果遇到短路操作,那么只要满足所有条件,流处理就会终止。

Java 惰性流 具体实例

读者在第一次接触流处理时,很容易认为流处理的效率不高。如例 3-65 所示,我们将 100 到 200 之间的所有整数倍增,然后找出能被 3 整除的第一个整数。

例 3-65 将 100 到 200 之间的所有整数倍增,再找出能被 3 整除的第一个整数

OptionalInt firstEvenDoubleDivBy3 = IntStream.range(100, 200)
    .map(n -> n * 2)
    .filter(n -> n % 3 == 0)
    .findFirst();
System.out.println(firstEvenDoubleDivBy3); ➊

➊ 打印 Optional[204]
如果不了解流处理的机制,读者可能认为上述代码做了不少无用功:

  • 创建 100 到 199 之间的整数(100 次操作)
  • 将每个整数倍增(100 次操作)
  • 校验每个整数能否被 3 整除(100 次操作)
  • 返回结果流的第一个元素(1 次操作)

那么,既然满足要求的第一个值为 204,为什么还要处理所有其他的数字呢?
不要误会,流处理的机制并非如此。流是惰性的,在达到终止条件前不会处理元素,达到终止条件后才通过流水线逐一处理每个元素。为说明这一点,例 3-66 对代码进行重构,以便读者观察每个元素通过流水线时的情况。

例 3-66 对每个流元素进行显式处理

public int multByTwo(int n) {      ➊
    System.out.printf("Inside multByTwo with arg %d%n", n);
    return n * 2;
}

public boolean divByThree(int n) { ➋
    System.out.printf("Inside divByThree with arg %d%n", n);
    return n % 3 == 0;
}

// 其他代码

firstEvenDoubleDivBy3 = IntStream.range(100, 200)
    .map(this::multByTwo)          ➊
    .filter(this::divByThree)      ➋
    .findFirst();

❶ 用于倍增(并打印)的方法引用
❷ 用于对 3 取模(并打印)的方法引用
例 3-66 的输出如下:

Inside multByTwo with arg 100
Inside divByThree with arg 200
Inside multByTwo with arg 101
Inside divByThree with arg 202
Inside multByTwo with arg 102
Inside divByThree with arg 204
First even divisible by 3 is Optional[204]

在本例中,100 被映射到 200,它未通过筛选器,流移至 101;101 被映射到 202,它同样未通过筛选器;下一个值 102 被映射到 204,它能被 3 整除,因此通过筛选器。流处理在仅处理三个值后即告终止,一共进行了 6 次操作。
这是流处理相对于直接处理集合的最大优点之一。对集合而言,必须执行完所有操作才能进行下一步操作。对流而言,各种中间操作构成了一条流水线,但流在达到终止操作前不会处理任何元素,达到终止操作后只处理所需的值。
流处理并非在任何情况下都有意义:如果进行任何状态操作(如排序或求和),就不得不处理所有值。但是,如果无状态操作后跟一个短路终止操作,流处理的优点还是很明显的。

赞(2)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

Java 实例