java - 并行流的 Stream.spliterator 的奇怪行为

标签 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);
    }
}

输出是

5
6
9

如您所见,在平面映射之后,我应该得到从 19 的连续数字的有序流。我拆分了一次拆分器,所以它应该跳到某个中间位置。接下来我从中消耗一个元素并再次拆分它。之后我打印所有剩余的元素。我希望我将有几个来自流尾的连续元素(可能是零个元素,也可以)。然而我得到的是 56,然后突然跳到 9

我知道目前在 JDK 中拆分器不是这样使用的:它们总是在遍历之前拆分。然而官方documentation没有明确禁止在 tryAdvance 之后调用 trySplit

当我使用直接从集合、数组、生成的源等创建的拆分器时,从未观察到该问题。只有当拆分器是从具有中间 flatMap 的并行流创建时才会观察到。

所以问题是:我是遇到了错误还是在某处明确禁止以这种方式使用拆分器?

最佳答案

来自 Spliterator.trySplit() 的文档:

This method may return null for any reason, including emptiness, inability to split after traversal has commenced, data structure constraints, and efficiency considerations.

(强调我的)

因此文档明确提到了在开始遍历后尝试拆分的可能性,并建议无法处理此问题的拆分器可能会返回 null

因此对于有序拆分器,观察到的行为应该被视为一个错误 as described by Misha .一般来说,trySplit() 必须返回一个 prefix 拆分器,换句话说,必须将所有关于下一个项目的中间状态移交给新的拆分器,这是Spliterator API 的一个特性,很可能会导致错误。我把这个问题作为检查我自己的拆分器实现的动机,并发现了一个类似的错误......

关于java - 并行流的 Stream.spliterator 的奇怪行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31152557/

相关文章:

java - DateTimeFormatter 无法在 Java 8 中解析秒后带小数点 0 的日期

java - 如何使用 Stream API 合并然后区分集合?

java - 使用 Java 8 将 int 数组转换为 long 数组?

java - 如何将断言添加到 if ..else 条件

java - 如何让 JAX-RS 返回 Java 8 LocalDateTime 属性作为 JavaScript 样式的日期字符串?

java - 将位于同一包层次结构下的 2 个类的日志消息打印到 2 个不同的文件

java - Kotlin 中的哪些语言功能可能导致内存泄漏?

java - 在封闭范围内定义的局部变量日志必须是最终的或实际上是最终的

java-8 - Java 8 : Elegant way to check that two or more variables are not null

Java流过滤器值的总和