【问题标题】:Strange behavior of Stream.spliterator for parallel streams并行流的 Stream.spliterator 的奇怪行为
【发布时间】:2015-09-18 02:14:09
【问题描述】:

我将流分离器直接用于我正在编写的库中的低级操作。最近,当我使用流拆分器并交错 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 的并行流创建时才观察到的。

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

【问题讨论】:

    标签: java java-8 java-stream spliterator


    【解决方案1】:

    这种行为被官方认定为一个错误(参见JDK-8148838),由我修复并推送到JDK-9 主干中(参见changeset)。可悲的是,我最初的补丁实际上修复了flatMap 之后的拆分(请参阅webrev),但是这个补丁被拒绝了,因为这种情况(在tryAdvance() 之后使用trySplit())被认为不常见并且不鼓励。目前公认的解决方案是在提前之后禁用WrappingSpliterator 拆分,这足以解决问题。

    【讨论】:

      【解决方案2】:

      来自Spliterator.trySplit()的文档:

      此方法可能出于任何原因返回null,包括空、遍历开始后无法拆分、数据结构限制和效率考虑。

      (强调我的)

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

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

      【讨论】:

        【解决方案3】:

        从我从AbstractWrappingSpliterator 和公司的来源可以看到,当您tryAdvance 时,flatMap 的输出(4,5,6)被缓冲,然后 4 被消耗离开(5,6)在缓冲区中。然后trySplit 正确地将 (7,8) 拆分为新的 Spliterator,将 9 留在旧的 Spliterator 中,但缓冲的 (5,6) 保留在旧的 Spliterator 中。

        所以这对我来说似乎是一个错误。它应该要么将缓冲区交给新的Spliterator,要么返回null,如果缓冲区不为空,则拒绝拆分。

        【讨论】:

          猜你喜欢
          • 2014-07-09
          • 1970-01-01
          • 2019-12-22
          • 1970-01-01
          • 1970-01-01
          • 2021-12-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多