不可分裂的分裂器

我试图了解Spliterator工作原理以及spliterator的设计方式。 我认识到trySplit()可能是Spliterator更重要的方法之一,但是当我看到一些第三方Spliterator实现时,有时我看到他们的分裂器无条件地为trySplit()返回null。

问题:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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