簡體   English   中英

Stream.spliterator對並行流的奇怪行為

[英]Strange behavior of Stream.spliterator for parallel streams

我正在使用流分裂器直接用於我正在編寫的庫中的低級操作。 最近,當我使用流分裂器和交錯tryAdvance/trySplit調用時,我發現了非常奇怪的行為。 這是一個簡單的代碼,演示了這個問題:

import java.util.Arrays;
import java.util.Spliterator;

public class SpliteratorBug {
    public static void main(String[] args) {
        Integer[][] input = { { 1 }, { 2, 3 }, { 4, 5, 6 }, { 7, 8 }, { 9 } };
        Spliterator<Integer> spliterator = Arrays.stream(input).parallel()
                .flatMap(Arrays::stream).spliterator();
        spliterator.trySplit();
        spliterator.tryAdvance(s -> {});
        spliterator.trySplit();
        spliterator.forEachRemaining(System.out::println);
    }
}

輸出是

5
6
9

正如您所看到的,在平面映射之后,我應該得到從19的連續數字的有序流。 我將分裂器分開一次,所以它應該跳到一些中間位置。 接下來我從中消耗一個元素並將其拆分一次。 之后我打印所有剩余的元素。 我希望我將從流尾部有幾個連續的元素(可能是零元素,它也會很好)。 然而我得到的是56 ,然后突然跳到9

我知道目前在JDK分裂器中並沒有這樣使用:它們總是在遍歷之前分裂。 但是官方文檔沒有明確禁止在trySplit之后調用tryAdvance

當我使用直接從集合,數組,生成的源等創建的spliterator時,從未觀察到這個問題。只有當spliterator是從具有中間flatMap的並行流創建時才會觀察到。

所以問題是:我是否遇到了這個錯誤,或者明確禁止某個地方以這種方式使用分裂器?

Spliterator.trySplit()的文檔:

此方法可能由於任何原因返回null ,包括空閑, 在遍歷開始后無法拆分 ,數據結構約束和效率考慮。

(強調我的)

因此,文檔明確提到在開始遍歷后嘗試拆分的可能性,並建議無法處理此問題的分裂器可能返回null

因此對於有序的分裂器,觀察到的行為應該被認為是Misha所描述的錯誤。 通常, trySplit()必須返回前綴 spliterator的事實,換句話說,必須將關於下一個項目的所有中間狀態移交給新的spliterator,這是Spliterator API的一個特性,它可能會產生錯誤。 我把這個問題作為檢查我自己的spliterator實現的動機,發現了類似的bug ...

從我從AbstractWrappingSpliterator和公司的源代碼中可以看出,當你tryAdvanceflatMap (4,5,6)的輸出被緩沖,然后4被消耗,留下(5,6)在緩沖區中。 然后trySplit正確分割(7,8)到新的Spliterator ,在舊的Spliterator留下9,但緩沖的(5,6)留在舊的Spliterator

所以這看起來像是一個錯誤。 它應該將緩沖區關閉到新的Spliterator或返回null並且如果緩沖區不為空則拒絕拆分。

這種行為被正式認定為一個錯誤(參見JDK-8148838 ),由我修復並推入JDK-9 trunk(參見changeset )。 令人遺憾的是,我的初始補丁實際上修復了flatMap之后的拆分(請參閱webrev ),但是這個補丁被拒絕了,因為這種情況(在trySplit()之后使用tryAdvance() )被認為是不常見tryAdvance()鼓勵。 當前接受的解決方案是在完全提前后禁用WrappingSpliterator拆分,這足以解決問題。

暫無
暫無

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

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