不可分裂的分裂器

时间:2015-03-05 02:45:03

标签: java java-8 spliterator

我正在尝试了解Spliterator的工作原理以及如何设计分裂器。我认识到trySplit()可能是Spliterator更重要的方法之一,但当我看到一些第三方Spliterator实现时,有时我会发现它们的分裂器为{{1}返回null无条件地。

问题:

  1. 普通迭代器和无条件返回null的trySplit()之间有区别吗?看起来这样的分裂者似乎打败了分裂。
  2. 当然,有一些合法的分裂器用例有条件地在Spliterator上返回null,但是有一个合理的分裂器用例无条件地返回null吗?

3 个答案:

答案 0 :(得分:4)

虽然Spliterator优于Iterator的主要优点是,正如你所说的,它的trySplit()方法允许它并行化,但还有其他显着优势:

  

http://docs.oracle.com/javase/8/docs/api/java/util/Spliterator.html

     

Spliterator API旨在通过支持分解和单元素迭代来支持有效的并行遍历以及顺序遍历。 此外,通过Spliterator访问元素的协议旨在实现比Iterator更小的每元素开销,并避免使用hasNext()和next()的单独方法所固有的竞争。

此外,可以使用StreamSupport.stream将Spliterators直接转换为Streams以使用Java8的流。

答案 1 :(得分:4)

Spliterator的目的之一是能够分裂,但这不是唯一的目的。另一个主要目的是作为创建自己的Stream源的支持类。创建Stream源的一种方法是实现自己的Spliterator并将其传递给StreamSupport.stream。最简单的方法是编写一个无法拆分的Spliterator。这样做会强制流按顺序执行,但这对于您尝试执行的任何操作都是可以接受的。

在其他情况下,编写不可拆分的Spliterator是有意义的。例如,在OpenJDK中,有EmptySpliterator等实现不包含任何元素。当然它不能拆分。类似的情况是singleton spliterator,其中只包含一个元素。它也不能拆分。两个实现都无条件地从null返回trySplit

另一种情况是编写不可拆分的Spliterator非常简单有效,实现可拆分Splite所需的代码量非常高。 (至少,不值得将一个写入Stack Overflow答案。)例如,请参阅this answer中的示例Spliterator。这里的情况是Spliterator实现想要包装另一个Spliterator并执行一些特殊操作,在这种情况下检查它是否为空。否则它只是将所有内容委托给包装的Spliterator。使用不可拆分的Spliterator执行此操作非常简单。

请注意,该答案中有讨论,my answer to the same question中有关该答案的评论,以及我的答案中的评论主题,关于如何制作可分割(即并行就绪)Spliterator。但实际上没有人写出代码进行拆分。 :-)根据你想要保留原始流的懒惰程度,以及你想要多少并行效率,编写一个可拆分的Spliterator会变得相当复杂。

在我的估计中,通过编写Iterator而不是Spliterator来做这类事情要容易一些(如我上面提到的答案)。事实证明Spliterators.spliteratorUnknownSize可以提供有限的并行性,即使是迭代器,它显然是纯粹的顺序构造。它在IteratorSpliterator内完成,它从Iterator中提取多个元素并批量处理它们。不幸的是,批量大小是硬编码的,但至少在某些情况下,这给了处理从Iterator并行处理的元素的机会。

答案 2 :(得分:2)

除了拆分支持之外,还有更多优势:

  • 迭代逻辑包含在单个tryAdvance方法中,而不是分布在hasNextnext这两种方法上。将逻辑划分为两个方法会使很多Iterator实现复杂化,因为它通常意味着hasNext方法必须执行实际的查询尝试,这可能会产生一个值,然后必须记住以下值-up next电话。而且,无论是显式还是隐式,都必须记住这个查询的事实。

    如果保证hasNext / next总是以典型的交替方式被调用,那将会更容易,但是,没有这样的保证。

    一个例子是BufferedReader.readLine(),它有一个简单的tryAdvance逻辑。换行Iterator必须在hasNext实现中调用该方法,并记住next调用的行。 (具有讽刺意味的是,当前的BufferedReader.stream()实现确实实现了这样一个复杂的Iterator,它将被包含在Spliterator中,而不是实现更简单的Spliterator直接。似乎不应低估“我不熟悉”这个问题。

  • estimateSize(); Spliterator可以返回可用于预分配资源的剩余项目的估计(或甚至确切数量)。这可以提高效率。

  • characteristics(); Spliterator可以提供有关其内容或行为的其他信息。除了告知估计的大小是否是精确大小之外,您还可以了解是否可以看到null值,是否存在已定义的遭遇顺序或所有值是否不同。特定算法可以利用这一点。显然,Stream API是可能利用的此类算法的集合,因此在计划创建(或支持创建)流并做出选择时,实现Spliterator告诉尽可能多的元信息优先于实现稍后将被包装的Iterator