[英]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
正如您所看到的,在平面映射之后,我應該得到從1
到9
的連續數字的有序流。 我將分裂器分開一次,所以它應該跳到一些中間位置。 接下來我從中消耗一個元素並將其拆分一次。 之后我打印所有剩余的元素。 我希望我將從流尾部有幾個連續的元素(可能是零元素,它也會很好)。 然而我得到的是5
和6
,然后突然跳到9
。
我知道目前在JDK分裂器中並沒有這樣使用:它們總是在遍歷之前分裂。 但是官方文檔沒有明確禁止在trySplit
之后調用tryAdvance
。
當我使用直接從集合,數組,生成的源等創建的spliterator時,從未觀察到這個問題。只有當spliterator是從具有中間flatMap
的並行流創建時才會觀察到。
所以問題是:我是否遇到了這個錯誤,或者明確禁止某個地方以這種方式使用分裂器?
此方法可能由於任何原因返回
null
,包括空閑, 在遍歷開始后無法拆分 ,數據結構約束和效率考慮。
(強調我的)
因此,文檔明確提到在開始遍歷后嘗試拆分的可能性,並建議無法處理此問題的分裂器可能返回null
。
因此對於有序的分裂器,觀察到的行為應該被認為是Misha所描述的錯誤。 通常, trySplit()
必須返回前綴 spliterator的事實,換句話說,必須將關於下一個項目的所有中間狀態移交給新的spliterator,這是Spliterator
API的一個特性,它可能會產生錯誤。 我把這個問題作為檢查我自己的spliterator實現的動機,發現了類似的bug ...
從我從AbstractWrappingSpliterator
和公司的源代碼中可以看出,當你tryAdvance
, flatMap
(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.