在Java Stream API中,中间操作是延迟执行的,而终端操作是急切执行的,这意味着什么? [英] What does it mean intermediate operations are lazily executed whereas terminal operations are eagerly executed in java Stream API?

查看:306
本文介绍了在Java Stream API中,中间操作是延迟执行的,而终端操作是急切执行的,这意味着什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

list.stream().filter( a-> a < 20 && a > 7).forEach(a -> System.out.println(a));

fiter被懒惰地执行.

forEach急切地被执行.

那是什么意思?

解决方案

假设您进行了以下操作.

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

中间操作是映射和过滤器,终端操作是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)

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

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

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

那有什么优势?

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

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

在这里我将类推范围缩小了一些,但是希望这可以帮助您更好地理解它.

list.stream().filter( a-> a < 20 && a > 7).forEach(a -> System.out.println(a));

fiter is lazily executed.

forEach is eagerly executed.

What does that mean?

解决方案

Say you had the following operation.

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

The intermediate operations are the maps and filters, the terminal operation is the forEach. If intermediate operations were eagerly executed, then .map(a -> a * a) would immediately map the whole stream and the result would be passed to .filter(a -> a > 0 && a < 10) which would immediately filter the result, which would then be passed to .map(a -> -a) which would map the filtered result and then pass it to forEach which would then immediately print each element from the stream.

However, intermediate operations are not eager, instead they are lazy. What this means is that the sequence

list.stream()
    .map(a -> a * a)
    .filter(a -> a > 0 && a < 100)
    .map(a -> -a)

does not actually do anything right away. It just creates a new stream that remembers the operations it is supposed to carry out, but does not actually carry them out until it is time to actually produce a result. It is not until forEach tries to read a value from the stream that it then goes to the original stream, takes a value, maps it using a -> a * a, filters it, and if it passes the filter, maps it using a -> -a and then passes that value to forEach.

It's like someone working in a restaurant that has been given the job of taking all the plates from the dirty pile, washing them, stacking them up and then giving them to the cook when he is ready to serve the food. If the person was eager, they would immediately take the whole pile of dirty plates, wash them all at once, and stack them up, then when the cook wants the plates, he hands them off one by one for serving.

However, a lazy employee would realize that the cook only needs one plate at a time, and only when the food is ready to serve. So when ever the cook needs a plate, the employee just takes one plate from the pile, washes it and hands it to the chef, going one by one until the plates are all washed and all the food is served.

So what's the advantage?

Well one major advantage is that the lazy approach considerably improves latency. As you are probably aware, a single thread of a program can only do one thing at a time. Extending the analogy a bit further, imagine there are about 800 plates, but the cook actually had to wait for the washer to finish washing the dishes and then hand one to him. If the eager washer insisted on washing all the plates first before handing any over, the cook would have to wait for all 800 plates to be washed, then serve 800 meals at once, by which point all the angry customers would have left.

However, with the lazy washer, for each meal the cook wants to serve, he only has to wait for one plate. So if washing a plate takes 10 seconds and serving is nearly instant, in scenario 1 all meals would be served at once but only after waiting for more than two hours. But in scenario 2, each meal is served about 10 seconds apart. So even though it takes the same amount of time to serve all meals, scenario 2 is certainly more desirable.

I've stretched the analogy a bit thin here, but hopefully this helps you understand it better.

这篇关于在Java Stream API中,中间操作是延迟执行的,而终端操作是急切执行的,这意味着什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆