流:连接后 toArray 崩溃
Streams: toArray crashing after 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
是从调用映射函数时填充的集合构建的。
似乎流 b
的源代码 collection 实现不正确,因为它使用 Collection.spliterator()
的默认实现,假定 Collection
在流期间未被修改操作(特别是它的 size()
不会改变)。在你的情况下,这似乎是错误的。
在没有看到您 collection 的完整代码的情况下,提出修复建议并不容易。如果在您的情况下可能,您可以在调用 size()
方法时执行初始化,因此在遍历 collection 之前调用 size()
会 return 正确的大小(目前它似乎 return 0)。另一种方法是像这样覆盖 spliterator()
方法:
public Spliterator<E> spliterator() {
return Spliterators.spliteratorUnknownSize(this.iterator(), Spliterator.ORDERED);
}
这样就不会报告 SIZED
特征,并且流管道不会依赖于大小。现在 toArray()
可能工作得更慢,因为可能需要重新分配数组,但它会正常工作。
我有一些代码如下所示:
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
是从调用映射函数时填充的集合构建的。
似乎流 b
的源代码 collection 实现不正确,因为它使用 Collection.spliterator()
的默认实现,假定 Collection
在流期间未被修改操作(特别是它的 size()
不会改变)。在你的情况下,这似乎是错误的。
在没有看到您 collection 的完整代码的情况下,提出修复建议并不容易。如果在您的情况下可能,您可以在调用 size()
方法时执行初始化,因此在遍历 collection 之前调用 size()
会 return 正确的大小(目前它似乎 return 0)。另一种方法是像这样覆盖 spliterator()
方法:
public Spliterator<E> spliterator() {
return Spliterators.spliteratorUnknownSize(this.iterator(), Spliterator.ORDERED);
}
这样就不会报告 SIZED
特征,并且流管道不会依赖于大小。现在 toArray()
可能工作得更慢,因为可能需要重新分配数组,但它会正常工作。