Java 不可抛撒分裂器
我试图了解拆分器是如何工作的,以及拆分器是如何设计的。我知道Java 不可抛撒分裂器,java,java-8,spliterator,Java,Java 8,Spliterator,我试图了解拆分器是如何工作的,以及拆分器是如何设计的。我知道trySplit()可能是Spliterator更重要的方法之一,但是当我看到一些第三方Spliterator实现时,有时我会看到它们的Spliterator无条件地为trySplit()返回null 问题是: 普通迭代器和无条件返回null的拆分器有何不同?看来这样的分裂者破坏了分裂的意义 当然,在trySplit()上有条件返回null的拆分器的合法用例,但是有无条件返回null的拆分器的合法用例吗 正如您所说,虽然Splitera
trySplit()
可能是Spliterator
更重要的方法之一,但是当我看到一些第三方Spliterator
实现时,有时我会看到它们的Spliterator无条件地为trySplit()
返回null
问题是:
trySplit()
上有条件返回null的拆分器的合法用例,但是有无条件返回null的拆分器的合法用例吗正如您所说,虽然Spliterator比Iterator的主要优点是它的trySplit()方法允许并行化,但它还有其他显著的优点: SpliteratorAPI通过支持分解和单元素迭代,除了支持顺序遍历外,还支持高效的并行遍历此外,通过拆分器访问元素的协议被设计为比迭代器施加更小的每元素开销,并避免为hasNext()和next()使用单独方法所涉及的固有竞争。
此外,拆分器可以直接转换为流,以利用Java8的流。拆分器的目的之一是能够拆分,但这不是唯一的目的。另一个主要用途是作为一个支持类来创建您自己的
流
源代码。创建流源的一种方法是实现自己的拆分器并将其传递给。最简单的方法通常是编写一个不能拆分的拆分器。这样做会强制流按顺序执行,但这对于您尝试执行的任何操作都是可以接受的
在其他情况下,编写不可拆分的拆分器是有意义的。例如,在OpenJDK中,有些实现(如)不包含任何元素。当然不能分割。类似的情况是只包含一个元素的。它也不能分割。两个实现都从trySplit
无条件返回null
另一种情况是,编写不可拆分的拆分器既简单又有效,而实现可拆分的拆分器所需的代码量是不允许的。(至少,不值得在堆栈溢出答案中写入一个)。这里的情况是,Spliterator实现想要包装另一个Spliterator并执行一些特殊的操作,在本例中,检查它是否为空。否则,它只是将所有内容委托给包装的拆分器。使用不可拆分的拆分器执行此操作非常简单
请注意,在该答案、该答案上的注释以及我答案上的注释线程中都有关于如何制作可拆分(即并行就绪)拆分器的讨论。但实际上没有人编写代码来进行拆分。:-)根据您希望从原始流保留多少惰性,以及您想要多少并行效率,编写可拆分拆分器可能会变得相当复杂
据我估计,通过编写迭代器而不是拆分器(正如我在上面的回答中提到的那样),可以更容易地完成这类工作。事实证明,
Spliterators.spliteratorUnknownSize
可以提供有限的并行性,甚至可以从迭代器中获得,迭代器显然是一种纯粹的顺序构造。它在内部执行,从迭代器中提取多个元素并成批处理它们。不幸的是,批大小是硬编码的,但至少在某些情况下,这为并行处理从迭代器中提取的元素提供了机会。除了拆分支持之外,还有更多的优势:
- 迭代逻辑包含在一个
方法中,而不是分散在两个方法中,如tryAdvance
,hasNext
。将逻辑拆分为两个方法会使许多next
实现复杂化,因为这通常意味着迭代器
方法必须执行实际的查询尝试,该查询尝试可能会产生一个值,然后在后续hasNext
调用中必须记住该值。而且必须记住这个查询已经被提出的事实,无论是显式的还是隐式的 如果有一个保证,下一次
/hasNext
总是以典型的交替方式调用,那么就更容易了,但是,没有这样的保证 一个例子是next
,它有一个简单的BufferedReader.readLine()
逻辑。包装tryAdvance
迭代器必须在
实现中调用该方法,并记住hasNext
调用的行。(讽刺的是,当前的next
实现确实实现了如此复杂的BufferedReader.stream()
,它将被包装到迭代器
中,而不是直接实现简单得多的拆分器
拆分器。似乎“我对此不熟悉”(问题不容低估)
;estimateSize()
可能会返回可用于预分配资源的剩余项目的估计值(甚至是确切数量)。这可以提高效率Spliterator
<代码>拆分器可以提供有关其内容或行为的附加信息。除了判断估计大小是否为精确大小外,还可以了解是否可以看到特性()
值,是否存在已定义的值null