在Java Stream API中,中间操作是延迟执行的而终端操作是急切执行的,这意味着什么?

The*_*hod 1 java java-stream

list.stream().filter( a-> a < 20 && a > 7).forEach(a -> System.out.println(a));
Run Code Online (Sandbox Code Playgroud)

fiter 懒惰地执行。

forEach 急切地被执行。

这意味着什么?

Leo*_*Aso 6

假设您进行了以下操作。

list.stream()
    .map(a -> a * a)
    .filter(a -> a > 0 && a < 100)
    .map(a -> -a)
    .forEach(a -> System.out.println(a));
Run Code Online (Sandbox Code Playgroud)

中间操作是映射和过滤器,终端操作是forEach。如果急切地执行了中间操作,.map(a -> a * a)则将立即映射整个流,并将结果传递到.filter(a -> a > 0 && a < 10)该流,该结果将立即过滤结果,然后将该结果传递到.map(a -> -a),将映射过滤后的结果,然后传递给forEach它,然后立即打印流中的每个元素。

但是,中间操作并不急切,而是懒惰的。这意味着顺序

list.stream()
    .map(a -> a * a)
    .filter(a -> a > 0 && a < 100)
    .map(a -> -a)
Run Code Online (Sandbox Code Playgroud)

实际上并没有立即做任何事情。它只是创建一个新的流,该流会记住应该执行的操作,但是直到实际产生结果时才真正执行它们。直到forEach尝试从流中读取一个值,然后它才转到原始流,获取一个值,使用对其进行映射,对其进行a -> a * a过滤,如果它通过了过滤器,则使用对其进行映射a -> -a,然后将该值传递给forEach

就像某人在一家餐馆里工作一样,他的工作是从肮脏的堆中拿走所有盘子,洗净,堆放起来,然后在准备提供食物时交给厨师。如果此人渴望,他们将立即拿起整堆脏盘子,一次洗净,再堆放起来,然后当厨师想要这些盘子时,他将它们一一递开。

但是,一个懒惰的员工会意识到,一次只需要准备一盘食物,厨师一次只需要一盘。因此,当厨师需要一盘时,员工只需从堆中拿出一个盘,将其洗净并交给厨师,就一步一步地进行操作,直到将盘全部洗净并提供所有食物为止。

那有什么好处呢?

好一个主要优点是,懒惰方法大大改善了延迟。您可能已经知道,程序的单个线程一次只能做一件事。进一步扩展类比,想象一下大约有800个盘子,但是厨师实际上不得不等待洗衣机洗完盘子再交给他。如果急切的洗衣机坚持要求在交出任何盘子之前先洗净所有盘子,那么厨师将不得不等待所有800个盘子被洗净,然后立即提供800顿饭,此时所有生气的顾客都会离开。

但是,有了懒惰的洗衣机,厨师想为每顿饭准备的饭菜,他只需要等待一盘即可。因此,如果洗盘子需要10秒钟并且几乎是立即上菜,那么在方案1中,所有餐点都可以立即上菜,但要等两个多小时。但是在方案2中,每顿饭的间隔时间约为10秒。因此,即使花相同的时间来提供所有餐点,方案2当然也是更可取的。

在这里,我已经将类推略作了延伸,但是希望这可以帮助您更好地理解它。

  • 那么一个接一个洗盘子比一次洗一个盘子有什么好处呢?结果是一样的 (2认同)
  • @TheScientificMethod,您将了解其中的区别,在老板进来之前洗了800块板并说:“好,我需要您数所有板”或“我所需要的就是知道其中是否有破损的板”…… (2认同)