3 回答

TA貢獻1757條經(jīng)驗 獲得超7個贊
假設(shè)您進行了以下操作。
list.stream()
.map(a -> a * a)
.filter(a -> a > 0 && a < 100)
.map(a -> -a)
.forEach(a -> System.out.println(a));
中間操作是映射和過濾器,終端操作是forEach. 如果急切地執(zhí)行中間操作,.map(a -> a * a)則將立即映射整個流,并將結(jié)果傳遞給.filter(a -> a > 0 && a < 10)which 將立即過濾結(jié)果,然后將傳遞給.map(a -> -a)which 將映射過濾后的結(jié)果,然后將其傳遞給forEach然后立即打印流中的每個元素。
然而,中間操作不是急切的,而是懶惰的。這意味著序列
list.stream()
.map(a -> a * a)
.filter(a -> a > 0 && a < 100)
.map(a -> -a)
實際上并沒有立即做任何事情。它只是創(chuàng)建一個新的流來記住它應(yīng)該執(zhí)行的操作,但直到實際產(chǎn)生結(jié)果時才真正執(zhí)行它們。直到forEach嘗試從流中讀取一個值,然后它才會轉(zhuǎn)到原始流,獲取一個值,使用 映射它a -> a * a,過濾它,如果它通過過濾器,映射它使用a -> -a,然后將該值傳遞給forEach。
這就像在餐廳工作的人被賦予了從臟堆中取出所有盤子,清洗它們,將它們堆疊起來,然后在廚師準備上菜時將它們交給廚師的工作。人急了,就立刻把整堆臟盤子拿起來,一下子洗干凈,疊好,等廚子要盤子時,一一遞給上菜。
然而,懶惰的員工會意識到廚師一次只需要一個盤子,而且只有在食物準備好時才需要。因此,當(dāng)廚師需要一個盤子時,員工只需從一堆盤子中取出一個盤子,清洗干凈并遞給廚師,一個接一個,直到所有盤子都洗干凈,所有的食物都端上來為止。
那么有什么好處呢?
一個主要優(yōu)點是惰性方法大大改善了延遲。您可能知道,程序的單個線程一次只能做一件事。進一步擴展類比,假設(shè)有大約 800 個盤子,但廚師實際上必須等待洗衣機洗完盤子,然后再將一個遞給他。如果熱心的洗碗工堅持先把盤子洗干凈再遞過來,廚師就得等著800個盤子都洗干凈了,然后一次上菜800頓,到時候憤怒的顧客都已經(jīng)離開了。
然而,有了懶惰的洗衣機,廚師每上菜,他只需要等一個盤子。因此,如果洗盤子需要 10 秒,而上菜幾乎是即時的,那么在場景 1 中,所有餐點都會一次上菜,但只有在等待兩個多小時后才能上菜。但在場景 2 中,每頓飯的供應(yīng)間隔約為 10 秒。因此,即使提供所有餐點所需的時間相同,場景 2 肯定更可取。
我在此將類比擴展了一點,但希望這可以幫助您更好地理解它。

TA貢獻1934條經(jīng)驗 獲得超2個贊
延遲執(zhí)行意味著操作只會在必要時執(zhí)行。
急切執(zhí)行意味著操作將立即執(zhí)行。
那么你可能會問什么時候執(zhí)行惰性中間操作?
當(dāng)對管道應(yīng)用終端操作(Eager 操作)時。
那么我們?nèi)绾沃酪粋€操作是中間的(懶惰的)還是終端的(急切的)呢?
當(dāng)操作返回一個Stream<T>
whereT
可以是任何類型時,它就是一個中間操作(懶惰);如果操作返回任何其他內(nèi)容,即 void、int、boolean 等,那么它是終端(急切)操作。

TA貢獻2016條經(jīng)驗 獲得超9個贊
在的JavaDoc的Stream說:
流是懶惰的;對源數(shù)據(jù)的計算僅在終端操作啟動時進行,源元素僅在需要時消費。
關(guān)于中間操作的JavaDoc:
他們總是懶惰;執(zhí)行中間操作,例如 filter()實際上并不執(zhí)行任何過濾,而是創(chuàng)建一個新的流,當(dāng)遍歷時,該流包含與給定謂詞匹配的初始流的元素。管道源的遍歷直到管道的終端操作被執(zhí)行后才開始。
由于map是一個惰性操作,以下代碼將不打印任何內(nèi)容:
Stream.of(1, 2, 3).map(i -> {
System.out.println(i);
return i;
});
這Stream缺少將執(zhí)行它的終端操作,這將調(diào)用中間操作。
類似list.stream().filter( a-> a > 20 && a < 7)將返回一個Stream但list尚未過濾的元素。
但即使執(zhí)行了終端操作,還有更多關(guān)于懶惰的問題:
懶惰還可以避免在不必要時檢查所有數(shù)據(jù);對于諸如“查找第一個長度超過 1000 個字符的字符串”之類的操作
如果需要執(zhí)行惰性操作來確定 a 的結(jié)果,則會執(zhí)行惰性操作Stream。并非來自源的所有元素都必須由惰性操作處理。
關(guān)于終端操作的 JavaDoc:
在幾乎所有情況下,終端操作都是急切的,在返回之前完成對數(shù)據(jù)源的遍歷和管道的處理。
此外,一個Stream.
執(zhí)行完終端操作后,流管道被認為已被消耗,不能再使用;
繼續(xù)這個例子:
long count = Stream.of(1, 2, 3).map(i -> {
System.out.println(i);
return i;
}).count();
確定count映射是無關(guān)緊要的。因此,此代碼仍然不會打印任何內(nèi)容。但由于count()是一個終端操作,流被處理并count獲得3分配的值。
如果我們將終端操作更改為,.min(Comparator.naturalOrder());則執(zhí)行所有映射,我們將看到打印的整數(shù)。
添加回答
舉報