為什麼在 Java Streams 中,filter() 在 flatMap() 之後不是「完全」惰性的?
在 Java 8 流中,中間操作如filter() 和 flatMap() 是惰性的,這表示它們在呼叫像 findFirst() 這樣的終端操作之前不會執行。然而,當 filter() 跟隨 flatMap() 時,這種惰性並沒有完全實現,從而導致意外的行為。
考慮這個程式碼範例:
Stream.of(1, 2, 3) .filter(i -> { System.out.println(i); return true; }) .findFirst() .get();
輸出顯示只有第一個列印了元素,表示filter()是懶惰的,並且在滿足條件時短路。相較之下:
Stream.of(1, 2, 3) .flatMap(i -> Stream.of(i - 1, i, i + 1)) .flatMap(i -> Stream.of(i - 1, i, i + 1)) .filter(i -> { System.out.println(i); return true; }) .findFirst() .get();
令人驚訝的是,所有元素都被印出來。即使 filter() 立即滿足其條件,它也會繼續處理流的後續元素。
原因
此行為是由於 Java Streams 實作造成的。在 flatMap() 之後,流不再包含原始元素。相反,它包含 flatMap() 產生的一系列扁平化元素。當filter()跟隨時,它對這些扁平化的元素進行操作,而不是原始流。
findFirst()中使用的forEachWithCancel()的實現,不斷地在spliterator上呼叫tryAdvance(),直到sink請求取消或 spliterator 已耗盡。在 flatMap() 的情況下,即使 filter() 已經找到匹配項,分割器也會繼續前進,而不會提前終止。
修正
這個問題已在 Java 10 中解決,並向後移植到 Java 8。現在,當像 filter() 這樣的短路操作被執行時,該實作允許在 flatMap() 中提前終止。
結論
可以理解的是,為什麼filter()在flatMap()之後表現得好像不是懶惰的,這可能會令人困惑。底層 Java Streams 實作規定了這種行為,後來在 Java 10 中解決了這個問題,並向後移植到 Java 8。這種理解對於有效處理此類場景至關重要。
以上是為什麼 Java Streams 中的「filter()」在「flatMap()」之後顯得不那麼懶惰?的詳細內容。更多資訊請關注PHP中文網其他相關文章!