亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

Java 8 流懶惰在實踐中無用嗎?

Java 8 流懶惰在實踐中無用嗎?

九州編程 2021-11-03 14:32:18
我最近閱讀了很多關于 Java 8 流的文章,還有幾篇關于使用 Java 8 流進行延遲加載的文章:here and over here。我似乎無法擺脫延遲加載完全沒用的感覺(或者充其量只是提供零性能價值的次要語法便利)。我們以這段代碼為例:int[] myInts = new int[]{1,2,3,5,8,13,21};IntStream myIntStream = IntStream.of(myInts);int[] myChangedArray = myIntStream                        .peek(n -> System.out.println("About to square: " + n))                        .map(n -> (int)Math.pow(n, 2))                        .peek(n -> System.out.println("Done squaring, result: " + n))                        .toArray();這將登錄控制臺,因為terminal operation在這種情況下toArray()調用的是 ,而我們的流是惰性的,僅在調用終端操作時才執行。當然我也可以這樣做:  IntStream myChangedInts = myIntStream    .peek(n -> System.out.println("About to square: " + n))    .map(n -> (int)Math.pow(n, 2))    .peek(n -> System.out.println("Done squaring, result: " + n));并且不會打印任何內容,因為地圖沒有發生,因為我不需要數據。直到我稱之為:  int[] myChangedArray = myChangedInts.toArray();瞧,我得到了映射數據和控制臺日志。除了我認為它的好處為零。我意識到我可以在調用 之前很久就定義過濾器代碼toArray(),并且我可以傳遞這個“未真正過濾的流),但那又怎樣?這是唯一的好處嗎?這些文章似乎暗示有與懶惰相關的性能提升,例如:在Java 8 Streams API中,中間操作是惰性的,并且對其內部處理模型進行了優化,使其能夠高性能地處理大量數據。和Java 8 Streams API 在短路操作的幫助下優化了流處理。短路方法在滿足條件后立即結束流處理。用正常的話短路操作,一旦條件滿足就會中斷所有中間操作,位于管道之前。一些中間和終端操作具有這種行為。從字面上看,這聽起來像是跳出一個循環,根本與懶惰無關。最后,在第二篇文章中有這句令人費解的臺詞:惰性操作實現效率。這是一種不處理陳舊數據的方法。在逐漸消耗輸入數據而不是預先擁有完整的元素集的情況下,延遲操作可能很有用。例如,考慮使用 Stream#generate(Supplier<T>) 創建無限流并且提供的供應商函數逐漸從遠程服務器接收數據的情況。在這種情況下,服務器調用只會在需要時在終端操作中進行。不處理陳舊數據?什么?延遲加載如何防止某人處理陳舊數據?TLDR:除了能夠在以后運行 filter/map/reduce/whatever 操作(這提供零性能優勢)之外,延遲加載還有什么好處嗎?如果是這樣,現實世界的用例是什么?
查看完整描述

3 回答

?
Helenr

TA貢獻1780條經驗 獲得超4個贊

您的終端操作 ,toArray()可能支持您的論點,因為它需要流的所有元素。


一些終端操作沒有。而對于這些,如果不是懶惰地執行流,那將是一種浪費。兩個例子:


//example 1: print first element of 1000 after transformations

IntStream.range(0, 1000)

    .peek(System.out::println)

    .mapToObj(String::valueOf)

    .peek(System.out::println)

    .findFirst()

    .ifPresent(System.out::println);


//example 2: check if any value has an even key

boolean valid = records.

    .map(this::heavyConversion)

    .filter(this::checkWithWebService)

    .mapToInt(Record::getKey)

    .anyMatch(i -> i % 2 == 0)

第一個流將打?。?/p>


0

0

0

也就是說,中間操作將只在一個元素上運行。這是一個重要的優化。如果不是懶惰,那么所有peek()調用都必須在所有元素上運行(絕對沒有必要,因為您只對一個元素感興趣)。中間操作可能很昂貴(例如在第二個示例中)


短路端子操作(toArray不是)使這種優化成為可能。


查看完整回答
反對 回復 2021-11-03
?
陪伴而非守候

TA貢獻1757條經驗 獲得超8個贊

惰性對于 API 的用戶非常有用,尤其是當Stream管道評估的最終結果可能非常大時!


簡單的例子是Files.linesJava API 本身中的方法。如果您不想將整個文件讀入內存并且只需要第一N行,那么只需編寫:


Stream<String> stream = Files.lines(path); // lazy operation


List<String> result = stream.limit(N).collect(Collectors.toList()); // read and collect


查看完整回答
反對 回復 2021-11-03
?
湖上湖

TA貢獻2003條經驗 獲得超2個贊

我有一個來自我們代碼庫的真實示例,因為我將對其進行簡化,但不完全確定您可能喜歡它還是完全掌握它...

我們有一個需要 的服務List<CustomService>,我想稱之為?,F在為了調用它,我要去一個數據庫(比現實簡單得多)并獲得一個List<DBObject>; 為了從中獲得a List<CustomService>,需要進行一些重大的轉換。

這是我的選擇,就地轉換并傳遞列表。很簡單,但可能不是那么理想。第二種選擇,重構服務,接受 aList<DBObject>和 a Function<DBObject, CustomService>。這聽起來微不足道,但它使懶惰(除其他外)成為可能。該服務有時可能只需要該 List 中的幾個元素,或者有時需要max某個屬性等 - 因此我不需要對所有元素進行大量轉換,這就是Stream API基于拉的懶惰是贏家的地方。

在 Streams 出現之前,我們曾經使用guava. 它也Lists.transform( list, function)很懶惰。

這不是流的基本特征,即使沒有番石榴也可以完成,但這樣要簡單得多。這里提供的例子findFirst很好,而且最容易理解;這就是懶惰的全部意義所在,元素僅在需要時才被拉取,它們不會從一個中間操作以塊的形式傳遞到另一個操作,而是一次從一個階段傳遞到另一個階段。


查看完整回答
反對 回復 2021-11-03
  • 3 回答
  • 0 關注
  • 203 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號