Tag*_*eev 10 java java-8 java-stream spliterator
我正在使用流分裂器直接用于我正在编写的库中的低级操作.最近,当我使用流分裂器和交错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);
}
}
Run Code Online (Sandbox Code Playgroud)
输出是
5
6
9
Run Code Online (Sandbox Code Playgroud)
正如你所看到的,后平映射我应该得到连续编号的有序流1来9.我将分裂器分开一次,所以它应该跳到一些中间位置.接下来我从中消耗一个元素并将其拆分一次.之后我打印所有剩余的元素.我希望我将从流尾部有几个连续的元素(可能是零元素,它也会很好).然而我所得到的是5和6,然后突然跳出来9.
我知道目前在JDK分裂器中并没有这样使用:它们总是在遍历之前分裂.但是官方文档没有明确禁止调用trySplit后tryAdvance.
当我使用直接从集合,数组,生成的源等创建的spliterator时,从未观察到这个问题.只有当spliterator是从具有中间体的并行流创建时才会观察到flatMap.
所以问题是:我是否遇到了这个错误,或者明确禁止某个地方以这种方式使用分裂器?
AbstractWrappingSpliterator从我从and company的来源可以看出,当你 时tryAdvance, (4,5,6) 的输出flatMap被缓冲,然后 4 被消耗,而 (5,6) 留在缓冲区中。然后trySplit正确地将 (7,8) 拆分为新的,Spliterator将 9 留在旧的中,但缓冲的 (5,6) 保留在旧的中Spliterator。
所以这对我来说看起来像是一个错误。它应该将缓冲区交给新缓冲区,或者如果缓冲区不为空则Spliterator返回并拒绝拆分。null
来自以下文件Spliterator.trySplit():
此方法可能
null由于任何原因返回,包括空闲,在遍历开始后无法拆分,数据结构约束和效率考虑.
(强调我的)
因此,文档明确提到在开始遍历后尝试拆分的可能性,并建议无法处理此问题的分裂器可能会返回null.
因此对于有序的分裂器,观察到的行为应该被认为是Misha所描述的错误.通常,trySplit()必须返回前缀分裂器的事实,换句话说,必须将关于下一个项目的所有中间状态移交给新的分裂器,这是Spliterator可能导致错误的API 的特性.我把这个问题作为检查我自己的spliterator实现的动机,发现了类似的bug ...