【问题标题】:Stream spliterator implementation detail流分离器实现细节
【发布时间】:2019-04-02 20:22:03
【问题描述】:

在查看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 手动将其“解析”成更小的部分。例如,您可以认为我正在尝试从Stream 执行findLast

这就是我想要分裂成更小块的愿望的地方,因为只要我这样做:

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

我发现prefixSplitnull,这意味着除了用forEachRemaning 消耗整个sp 之外,我基本上无能为力。

这有点奇怪,可能filter 出现时它是有意义的;因为在这种情况下(在我的理解中)可以返回Spliterator 的唯一方法是使用某种buffer,甚至可能具有预定义的大小(很像Files::lines)。但为什么会这样:

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

返回null 是我不明白的。 sorted 是一种有状态的操作,它无论如何都会缓冲元素,而不会实际减少或增加它们的初始数量,所以至少理论上这可以返回 null 以外的东西...

【问题讨论】:

  • 嗯...可能是因为流不并行?明显的问题......你试过Arrays.asList(1,2,3,4).parallelStream()......吗?
  • @FedericoPeraltaSchaffner 当然... :) 但是如果您删除 filter 并且流也不是并行的,则拆分将起作用。文档没有说这必须发生在并行流中。
  • from javadoc: *

    此方法可能因任何原因返回{@code null},*包括空虚、遍历开始后无法拆分、数据结构约束和效率*注意事项。

  • 当您不链接中间操作时,spliterator() 将只返回源拆分器,即Arrays.asList(1,2,3,4,5) .stream() .spliterator() .getClass() == Arrays.spliterator(new Integer[] { 1,2,3,4,5 }) .getClass()。这样的拆分器甚至不知道 Stream 是否是并行的,或者 Stream 是否存在。不,Arrays.asList(1,2,3,4,5) .parallelStream() .filter(x -&gt; x != 1) .spliterator();不需要需要任何缓冲。
  • @Holger 对,我已经看到如果没有中间操作,则返回源拆分器,即使是 WrappingSpliterator 通过spliteratorSupplier“知道”它。第二点是我认为它可能是用缓冲区实现的,我现在查看了实现,你是对的,它拆分了源拆分器......所以在这种情况下的问题是这是对顺序流的深思熟虑的决定吗?你知道这其中的原因吗?谢谢

标签: java java-8 java-stream


【解决方案1】:

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

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

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

对于无状态的中间操作,它不会有所作为。但是,正如“Why the tryAdvance of stream.spliterator() may accumulate items into a buffer?”中所讨论的,WrappingSpliterator 是一个“万能实现”,它没有考虑管道的实际性质,因此它的限制是所有支持的所有可能限制的超集管道阶段。因此,在忽略parallel 标志时存在一种无法工作的场景足以禁止在不是parallel 时对所有管道进行拆分。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-12-21
    • 1970-01-01
    • 1970-01-01
    • 2016-10-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多