2015-08-18 33 views
0

後指定者崩潰我有一些代碼看起來像這樣:流:CONCAT

Stream a = Streams.from(...).map(...); 
Stream b = Streams.from(...); 
Stream c = Stream.concat(a, b); 
c.toArray(); 

Streams.from(Iterable)創建從一個可迭代流 - 被視爲Iterable#stream()不存在)

最後一行崩潰但有:

Exception in thread "main" java.lang.IllegalStateException: Accept exceeded fixed size of 266 
    at java.util.stream.Nodes$FixedNodeBuilder.accept(Nodes.java:1224) 
    at java.util.Iterator.forEachRemaining(Iterator.java:116) 
    at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801) 
    at java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:743) 
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:512) 
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:502) 
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:576) 
    at java.util.stream.AbstractPipeline.evaluateToArrayNode(AbstractPipeline.java:255) 
    at java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:438) 
    at ... (my code) 

266實際上是流的大小a。流b是11個元素。

我做了一些測試。從代碼中刪除map(...)使其工作。 下面的小例子也可以工作:

Stream x = Stream.of(1, 2).map(w -> w + 1); 
Stream y = Stream.of(3, 4); 
Stream z = Stream.concat(x, y); 
z.toArray(); 

在這一點上我是非常懷疑我的映射功能,然而,這只是正常工作:

Object[] a = Streams.from(...).map(...).toArray(); 
Object[] b = Streams.from(...).toArray(); 
Object[] c = ArrayUtils.addAll(a, b); 

是否有可能解釋這種怪異的流API微妙行爲?

提到流b是從調用映射函數時被填充的集合構建而成。

+2

_它可能是相關的提到,流b是由一個集合構建的,當映射函數被調用時被填充._請告訴我們。提供一個MCVE。 –

+3

什麼是Streams.from()或Stream.from()?這些不在JDK API中。另外,你的任何實現都有自己的分割器嗎?如果是這樣,請問你能表明一下嗎? –

+3

*可能有必要提到的是,流b是由調用映射函數時被填充的集合構建的。在調用映射函數之前,不會填充底層流b的集合,直到執行c.toArray()時纔會調用映射函數(因爲流是懶惰尋求的)。因此,在流操作時正在修改其中一個流源,這違反了流規範的非干擾要求。 –

回答

1

看來,因爲它使用的Collection.spliterator()默認實現其假定Collection不在流操作過程中修改(特別是其size()不會改變)源收集流b被錯誤地執行。在你的情況下,這似乎是錯誤的。

沒有看到您的收藏的完整代碼,建議修復並不是那麼容易。如果可能的話,您可以在調用size()方法時執行初始化,因此在遍歷集合之前調用size()將返回正確的大小(目前它似乎返回0)。另一種方法是重寫spliterator()方法是這樣的:

public Spliterator<E> spliterator() { 
    return Spliterators.spliteratorUnknownSize(this.iterator(), Spliterator.ORDERED); 
} 

這樣的SIZED特點不報告和數據流管道將不依賴於規模。現在toArray()可能會工作得更慢,因爲數組重新分配可能變得必要,但它會正常工作。

+1

問題描述是正確的,解決方案不是。修改源收集仍然違反合同。沒有保證特定集合的分割器將調用'size()'方法來找出它自己的大小。當使用'spliteratorUnknownSize'時,你不會得到關於固定大小的異常,但可能會得到'Iterator'拋出的'ConcurrentModificationException'。即使你沒有得到例外,這不是一個解決方案,但只是一個討厭的解決方法。實施細節發生變化時可能突然失敗。 – Holger

+0

@Holger,根據我的假設,解決方案是正確的(儘管這可能不正確)。我假設OP擴展了'AbstractCollection'或'AbstractList',並且在迭代開始時(在首次調用'iterator()'或'get(n)')時,集合被完全填充並且不再改變。希望OP會提供更多的細節來使猜測不必要... –

+1

OP明確指出映射函數操縱第二個流的源集合。當函數被評估時,已經調用了'iterator()'*,因爲'stream()'的默認實現將立即調用'spliterator()',並在調用'Stream.concat(...)'之前發生。 – Holger