流分离器实现细节

时间:2019-04-02 20:22:03

标签: java java-8 java-stream

在查看WrappingSpliterator::trySplit的源代码时,我对其实现很误解:

    @Override
    public Spliterator<P_OUT> trySplit() {
        if (isParallel && buffer == null && !finished) {
            init();

            Spliterator<P_IN> split = spliterator.trySplit();
            return (split == null) ? null : wrap(split);
        }
        else
            return null;
    }

如果您想知道为什么这很重要,是因为例如:

Arrays.asList(1,2,3,4,5)
      .stream()
      .filter(x -> x != 1)
      .spliterator();

正在使用它。以我的理解,在流中添加任何中间操作都将导致该代码被触发。

基本上,此方法表示除非流是并行的,否则将此Spliterator视为根本无法拆分的分离器。这对我很重要。在我的一种方法(这就是我获得该代码的方式)中,我得到了Stream作为输入,并使用trySplit手动将其“解析”成小块。例如,您可以认为我正在尝试从findLast做一个Stream

在这里,我想要分裂成小块的愿望被否定了,因为我一这样做:

Spliterator<T> sp = stream.spliterator();
Spliterator<T> prefixSplit = sp.trySplit();

我发现prefixSplitnull,这意味着除了使用sp消耗整个forEachRemaning之外,我基本上不能做其他事情。

这有点奇怪,可能是 对于出现filter时是有道理的;因为在这种情况下(据我所知),返回Spliterator的唯一方法是使用某种buffer,甚至可能具有预定义的大小(与Files::lines相似)。但是为什么这样:

Arrays.asList(1,2,3,4)
      .stream()
      .sorted()
      .spliterator()
      .trySplit();

返回null是我不了解的事情。 sorted是一个有状态操作,无论如何都将缓冲元素,而实际上并没有减少或增加它们的初始数目,因此至少在理论上,这可以返回null ...

以外的内容。

1 个答案:

答案 0 :(得分:1)

spliterator()上调用Stream时,当前的实现只有两种可能的结果。

如果流没有中间操作,您将获得用于构造流的源拆分器,其拆分能力完全独立于流的并行状态,实际上,拆分器不了解任何信息流。

否则,您将获得WrappingSpliterator,它将封装源Spliterator和管道状态,表示为PipelineHelperSpliteratorPipelineHelper的这种组合不需要并行工作,实际上,在distinct()的情况下是行不通的,因为WrappingSpliterator将会完全不同组合,具体取决于Stream是否并行。

对于无状态的中间操作,这没有什么区别。但是,正如“ Why the tryAdvance of stream.spliterator() may accumulate items into a buffer?”中讨论的那样,WrappingSpliterator是“万能的实现”,它没有考虑管道的实际性质,因此其局限性是所有可能的超集所有支持的管道阶段的局限性。因此,存在一种在忽略parallel标志时不起作用的情况,足以禁止在没有parallel时对所有管道进行拆分。