在分析一个同事几年前的 Java 7 代码时,我发现他实现了一种用于遍历数据(可能是并行的)的工具。他称之为
通过递归函数,我们可以将其分成
也可以说要避免与 spliterator 搞在一起,让流在实际处理过程中进行 fork 所引发的分裂,但 ForkJoin 策略可能不太适合此任务,并且不能保证会使用我们专门为该工作分配的线程数。事实上,有可能在少量元素上执行繁重任务。
问题概括为:如果有一个至少具备 SIZED 和 SUBSIZED 特征的 spliterator,如何将其分成精确数量的 spliterator?
Range
,并扩展了 Iterator
接口。它的一些新方法有些奇怪地熟悉:
int size()
可以给出范围的确切大小;Range split()
可以将范围分成两个部分,最好是相似的大小(修改当前范围并创建一个新范围);Range[] split(int n)
可以将范围分成 N 个子范围,可能会尝试使它们尽可能均匀。- 尽管
Iterator
中有void remove()
,但其子类型只是会抛出UnsupportedOperationException
。
n
部分的简单方法。通过递归函数,我们可以将其分成
n=2^L
部分,其中 L
是递归级别,但当 n
不是两的幂时,这种方法并不那么简单,更不用说保留一个工具函数的效果了。也可以说要避免与 spliterator 搞在一起,让流在实际处理过程中进行 fork 所引发的分裂,但 ForkJoin 策略可能不太适合此任务,并且不能保证会使用我们专门为该工作分配的线程数。事实上,有可能在少量元素上执行繁重任务。
问题概括为:如果有一个至少具备 SIZED 和 SUBSIZED 特征的 spliterator,如何将其分成精确数量的 spliterator?
ArrayList
支持,要么由 Lucene 的IndexReader
支持。提前了解这些细节肯定会提高性能,但是在这里拥有一个通用的解决方案也是不错的。 - E net4IndexReader
支持的并行化经验(这实际上也是我编写这些类的原因)。由于Lucene操作是重量级的(100微秒的查找并不罕见),即使在顺序模式下并行化也是值得的。请注意,在这种情况下,将核心数与批次数精确匹配并不那么重要。给每个批次足够的工作量(例如10毫秒),协调/切换开销就会消失。 - Marko Topolnik