簡體   English   中英

Java 8 Stream API - 任何有狀態的中間操作都能保證新的源集合嗎?

[英]Java 8 Stream API - Does any stateful intermediate operation guarantee a new source collection?

以下陳述是真的嗎?

sorted()操作是“有狀態的中間操作”,這意味着后續操作不再對后備集合進行操作,而是對內部狀態進行操作。

來源來源 - 他們似乎互相復制或來自同一來源。)

我已經測試了Stream::sorted作為上述來源的片段:

final List<Integer> list = IntStream.range(0, 10).boxed().collect(Collectors.toList());

list.stream()
    .filter(i -> i > 5)
    .sorted()
    .forEach(list::remove);

System.out.println(list);            // Prints [0, 1, 2, 3, 4, 5]

有用。 我用Stream::distinctStream::limitStream::skip替換了Stream::sorted

final List<Integer> list = IntStream.range(0, 10).boxed().collect(Collectors.toList());

list.stream()
    .filter(i -> i > 5)
    .distinct()
    .forEach(list::remove);          // Throws NullPointerException

令我驚訝的是,拋出了NullPointerException

所有測試方法都遵循有狀態中間操作特性。 然而, Stream::sorted這種獨特行為沒有記錄, Stream操作和管道部分也解釋了有狀態中間操作是否真正保證了新的源集合。

我的困惑來自何處以及上述行為的解釋是什么?

API文檔沒有保證“后續操作不再對后備集合進行操作”,因此,您永遠不應該依賴於特定實現的這種行為。

你的例子偶然發生了想要的事情; 甚至不能保證collect(Collectors.toList())創建的List支持remove操作。

顯示一個反例

Set<Integer> set = IntStream.range(0, 10).boxed()
    .collect(Collectors.toCollection(TreeSet::new));
set.stream()
    .filter(i -> i > 5)
    .sorted()
    .forEach(set::remove);

拋出ConcurrentModificationException 原因是實現優化了這種情況,因為源已經排序。 原則上,它可以對原始示例執行相同的優化,因為forEach以無指定順序顯式執行操作,因此,排序是不必要的。

還有其他可以想象的優化,例如sorted().findFirst()可以轉換為“查找最小”操作,而無需將元素復制到新存儲中進行排序。

因此,最重要的是,當依賴於未指明的行為時,今天可能發生的事情可能會在明天,即添加新的優化時中斷。

sorted好后必須是流管道的完整復制屏障,因為所有源都無法排序 ; 但這並沒有記錄,因此不依賴它。

不僅僅是關於sorted本身,而是可以對流管道進行其他優化,以便可以完全跳過sorted 例如:

List<Integer> sortedList = IntStream.range(0, 10)
            .boxed()
            .collect(Collectors.toList());

    StreamSupport.stream(() -> sortedList.spliterator(), Spliterator.SORTED, false)
            .sorted()
            .forEach(sortedList::remove); // fails with CME, thus no copying occurred 

當然, sorted需要是一個完整的障礙並停止進行整個排序,當然,除非它可以被跳過,因此文檔沒有做出這樣的承諾,因此我們不會遇到奇怪的意外。

distinct另一方面不必是完整的屏障 ,所有不同的作用是在一個時間檢查一個元件,如果是唯一的; 因此,在檢查單個元素(並且它是唯一的)之后,它將被傳遞到下一個階段,因此不會成為完整的障礙。 無論哪種方式,這也沒有記錄......

你不應該通過forEach(list::remove)的終端操作提起案例,因為list::remove是一個干擾函數,它違反了終端動作的“非干擾”原則。

在了解為什么不正確的代碼段導致意外(或未記錄)行為之前,遵循規則至關重要。

我相信list::remove是問題的根源。 如果你為forEach寫了一個合適的動作,你就不會注意到這個場景的操作之間的區別。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM