- 类型参数:
T
- 此 Spliterator 返回的元素类型
- 所有已知的子接口:
Spliterator.OfDouble
,Spliterator.OfInt
,Spliterator.OfLong
,Spliterator.OfPrimitive<T,
T_CONS, T_SPLITR>
- 所有已知的实现类:
Spliterators.AbstractDoubleSpliterator
,Spliterators.AbstractIntSpliterator
,Spliterators.AbstractLongSpliterator
,Spliterators.AbstractSpliterator
Collection
、IO 通道或生成器函数。
Spliterator 可以单独遍历元素(tryAdvance()
)或批量遍历元素(forEachRemaining()
)。
Spliterator 也可以将它的一些元素(使用 trySplit()
)划分为另一个 Spliterator,用于可能并行的操作。使用不能拆分的 Spliterator 的操作,或者以高度不平衡或低效的方式进行拆分,不太可能从并行性中受益。横向和分离式排气元件;每个 Spliterator 仅对单个批量计算有用。
Spliterator 还报告一组 characteristics()
的结构、来源和来自 ORDERED
、DISTINCT
、SORTED
、SIZED
、NONNULL
、IMMUTABLE
、CONCURRENT
和 SUBSIZED
的元素。 Spliterator 客户端可以使用这些来控制、专门化或简化计算。例如,Collection
的 Spliterator 会报告 SIZED
,Set
的 Spliterator 会报告 DISTINCT
,SortedSet
的 Spliterator 也会报告 SORTED
。特征被报告为一个简单的联合位集。一些特征额外地限制了方法行为;例如,如果 ORDERED
,遍历方法必须符合其记录的顺序。将来可能会定义新的特征,因此实施者不应为未列出的值赋予含义。
不报告 IMMUTABLE
或 CONCURRENT
的 Spliterator 应该有一个记录的策略,涉及:当 spliterator binds 到元素源时;结合后检测元素源的结构干扰检测。 A late-binding Spliterator 在第一次遍历、第一次拆分或第一次查询估计大小时绑定到元素源,而不是在创建 Spliterator 时。不是 late-binding 的 Spliterator 在构造或首次调用任何方法时绑定到元素源。在遍历 Spliterator 时反映在绑定之前对源所做的修改。绑定 Spliterator 后,如果检测到结构干扰,应尽最大努力抛出 ConcurrentModificationException
。执行此操作的拆分器称为 fail-fast 。 Spliterator 的批量遍历方法 (forEachRemaining()
) 可以优化遍历并在遍历所有元素后检查结构干扰,而不是检查每个元素并立即失败。
拆分器可以通过 estimateSize()
方法提供对剩余元素数量的估计。理想情况下,如特性 SIZED
中所反映的,此值与成功遍历中遇到的元素数完全对应。然而,即使不完全知道,估计值可能仍然对在源上执行的操作有用,例如帮助确定是进一步拆分还是顺序遍历剩余元素更可取。
尽管它们在并行算法中具有明显的实用性,但拆分器并不期望是线程安全的;相反,使用拆分器的并行算法的实现应确保拆分器一次仅由一个线程使用。这通常很容易通过 serial thread-confinement 实现,这通常是通过递归分解工作的典型并行算法的自然结果。调用 trySplit()
的线程可能会将返回的 Spliterator 交给另一个线程,而另一个线程可能会遍历或进一步拆分该 Spliterator。如果两个或多个线程同时操作同一个拆分器,则拆分和遍历的行为是未定义的。如果原始线程将拆分器移交给另一个线程进行处理,则最好在使用 tryAdvance()
使用任何元素之前进行切换,因为某些保证(例如 estimateSize()
对于 SIZED
拆分器的准确性)仅在遍历开始之前有效.
为 int
、long
和 double
值提供了 Spliterator
的原始子类型特化。 tryAdvance(java.util.function.Consumer)
和 forEachRemaining(java.util.function.Consumer)
框原始值的子类型默认实现为其相应包装类的实例。这种装箱可能会破坏通过使用原始专业化获得的任何性能优势。为避免装箱,应使用相应的基于原语的方法。例如,Spliterator.OfPrimitive.tryAdvance(java.util.function.IntConsumer)
和 Spliterator.OfPrimitive.forEachRemaining(java.util.function.IntConsumer)
应优先于 Spliterator.OfInt.tryAdvance(java.util.function.Consumer)
和 Spliterator.OfInt.forEachRemaining(java.util.function.Consumer)
使用。使用基于装箱的方法 tryAdvance()
和 forEachRemaining()
遍历原始值不会影响遇到转换为装箱值的值的顺序。
- API 注意:
-
拆分器,如
Iterator
s,用于遍历源的元素。Spliterator
API 旨在通过支持分解和单元素迭代来支持高效的并行遍历和顺序遍历。此外,通过 Spliterator 访问元素的协议旨在施加比Iterator
更小的每个元素开销,并避免为hasNext()
和next()
使用单独的方法所涉及的固有竞争。对于可变源,如果在 Spliterator 绑定到其数据源和遍历结束之间源在结构上受到干扰(添加、替换或删除元素),则可能会发生任意和非确定性行为。例如,在使用
java.util.stream
框架时,此类干扰会产生任意的、不确定的结果。源的结构性干扰可以通过以下方式进行管理(按可取性递减的大致顺序):
- 来源不能在结构上受到干扰。
例如,CopyOnWriteArrayList
的实例是不可变源。从源创建的 Spliterator 报告了IMMUTABLE
的特征。 - 源管理并发修改。
例如,ConcurrentHashMap
的键集是并发源。从源创建的 Spliterator 报告了CONCURRENT
的特征。 - 可变源提供了一个后期绑定和快速失败的 Spliterator。
后期绑定缩小了干扰影响计算的窗口; fail-fast 在最大努力的基础上检测到在遍历开始后发生了结构干扰并抛出ConcurrentModificationException
。例如,ArrayList
和 JDK 中的许多其他非并发Collection
类提供了一个后期绑定、快速失败的拆分器。 - 可变源提供了一个非后期绑定但快速失败的 Spliterator。
由于潜在干扰的窗口更大,源增加了抛出ConcurrentModificationException
的可能性。 - 可变源提供了一个后期绑定和非快速失败的 Spliterator。
由于未检测到干扰,在开始遍历后,源可能会出现任意的、不确定的行为。 - 可变源提供了一个非后期绑定和非快速失败的 Spliterator。
源增加了任意、不确定行为的风险,因为在构建之后可能会发生未检测到的干扰。
例子。这是一个类(不是很有用,除了说明),它维护一个数组,其中实际数据保存在偶数位置,不相关的标签数据保存在奇数位置。它的 Spliterator 会忽略标签。
class TaggedArray<T> { private final Object[] elements; // immutable after construction TaggedArray(T[] data, Object[] tags) { int size = data.length; if (tags.length != size) throw new IllegalArgumentException(); this.elements = new Object[2 * size]; for (int i = 0, j = 0; i < size; ++i) { elements[j++] = data[i]; elements[j++] = tags[i]; } } public Spliterator<T> spliterator() { return new TaggedArraySpliterator<>(elements, 0, elements.length); } static class TaggedArraySpliterator<T> implements Spliterator<T> { private final Object[] array; private int origin; // current index, advanced on split or traversal private final int fence; // one past the greatest index TaggedArraySpliterator(Object[] array, int origin, int fence) { this.array = array; this.origin = origin; this.fence = fence; } public void forEachRemaining(Consumer<? super T> action) { for (; origin < fence; origin += 2) action.accept((T) array[origin]); } public boolean tryAdvance(Consumer<? super T> action) { if (origin < fence) { action.accept((T) array[origin]); origin += 2; return true; } else // cannot advance return false; } public Spliterator<T> trySplit() { int lo = origin; // divide range in half int mid = ((lo + fence) >>> 1) & ~1; // force midpoint to be even if (lo < mid) { // split out left half origin = mid; // reset this Spliterator's origin return new TaggedArraySpliterator<>(array, lo, mid); } else // too small to split return null; } public long estimateSize() { return (long)((fence - origin) / 2); } public int characteristics() { return ORDERED | SIZED | IMMUTABLE | SUBSIZED; } } }
作为并行计算框架(如
java.util.stream
包)如何在并行计算中使用 Spliterator 的示例,这里是实现关联并行 forEach 的一种方法,它说明了拆分子任务直到估计工作量的主要用法足够小,可以顺序执行。这里我们假设跨子任务的处理顺序无关紧要;不同的(分叉的)任务可能会以未确定的顺序进一步拆分和并发处理元素。此示例使用CountedCompleter
;类似的用法适用于其他并行任务构造。static <T> void parEach(TaggedArray<T> a, Consumer<T> action) { Spliterator<T> s = a.spliterator(); long targetBatchSize = s.estimateSize() / (ForkJoinPool.getCommonPoolParallelism() * 8); new ParEach(null, s, action, targetBatchSize).invoke(); } static class ParEach<T> extends CountedCompleter<Void> { final Spliterator<T> spliterator; final Consumer<T> action; final long targetBatchSize; ParEach(ParEach<T> parent, Spliterator<T> spliterator, Consumer<T> action, long targetBatchSize) { super(parent); this.spliterator = spliterator; this.action = action; this.targetBatchSize = targetBatchSize; } public void compute() { Spliterator<T> sub; while (spliterator.estimateSize() > targetBatchSize && (sub = spliterator.trySplit()) != null) { addToPendingCount(1); new ParEach<>(this, sub, action, targetBatchSize).fork(); } spliterator.forEachRemaining(action); propagateCompletion(); } }
- 来源不能在结构上受到干扰。
- 实现注意事项:
-
如果布尔系统属性
org.openjdk.java.util.stream.tripwire
设置为true
,则在对原始子类型特化进行操作时发生原始值装箱时会报告诊断警告。 - 自从:
- 1.8
- 参见:
-
内部类总结
内部类修饰符和类型接口描述static interface
专门用于double
值的 Spliterator。static interface
专用于int
值的 Spliterator。static interface
专门用于long
值的 Spliterator。static interface
专门用于原始值的 Spliterator。 -
字段摘要
字段修饰符和类型Field描述static final int
特征值表示元素源可以在没有外部同步的情况下由多个线程安全地并发修改(允许添加、替换和/或删除)。static final int
特征值表示,对于每对遇到的元素x, y
,!x.equals(y)
。static final int
表示元素源不能进行结构修改的特征值;也就是说,不能添加、替换或删除元素,因此在遍历期间不会发生此类更改。static final int
表示源保证遇到的元素不会是null
的特征值。static final int
表示为元素定义了相遇顺序的特征值。static final int
特征值表示在遍历或拆分之前从estimateSize()
返回的值表示有限大小,在没有结构源修改的情况下,表示完整遍历将遇到的元素数量的精确计数。static final int
表示遇到顺序遵循定义的排序顺序的特征值。static final int
-
方法总结
修饰符和类型方法描述int
返回此 Spliterator 及其元素的一组特征。long
返回forEachRemaining(java.util.function.Consumer<? super T>)
遍历将遇到的元素数量的估计值,或者如果无限、未知或计算成本太高则返回Long.MAX_VALUE
。default void
forEachRemaining
(Consumer<? super T> action) 在当前线程中按顺序对每个剩余元素执行给定操作,直到处理完所有元素或操作引发异常。default Comparator<? super T>
default long
default boolean
hasCharacteristics
(int characteristics) 如果此 Spliterator 的characteristics()
包含所有给定特征,则返回true
。boolean
tryAdvance
(Consumer<? super T> action) 如果存在剩余元素,则对其执行给定的操作,返回true
;否则返回false
。trySplit()
如果此 spliterator 可以分区,则返回一个 Spliterator 重写元素,从该方法返回时,该 Spliterator 将不被此 Spliterator 重写。
-
字段详细信息
-
ORDERED
static final int ORDERED表示为元素定义了相遇顺序的特征值。如果是这样,则此 Spliterator 保证方法trySplit()
拆分元素的严格前缀,该方法tryAdvance(java.util.function.Consumer<? super T>)
按前缀顺序逐个元素,并且forEachRemaining(java.util.function.Consumer<? super T>)
按遇到顺序执行操作。如果相应的
Collection.iterator()
记录了一个命令,则Collection
有一个遇到命令。如果是这样,遇到的顺序与记录的顺序相同。否则,集合没有遇到顺序。 -
DISTINCT
static final int DISTINCT- 参见:
-
SORTED
static final int SORTED表示遇到顺序遵循定义的排序顺序的特征值。如果是这样,方法getComparator()
返回关联的比较器,或者null
如果所有元素都是Comparable
并且按它们的自然顺序排序。报告
SORTED
的 Spliterator 也必须报告ORDERED
。- API 注意:
-
JDK 中实现
NavigableSet
或SortedSet
的Collection
类的拆分器报告SORTED
。 - 参见:
-
SIZED
static final int SIZED特征值表示在遍历或拆分之前从estimateSize()
返回的值表示有限大小,在没有结构源修改的情况下,表示完整遍历将遇到的元素数量的精确计数。- API 注意:
-
大多数涵盖
Collection
的所有元素的 Collections Spliterators 报告了此特征。子拆分器(例如HashSet
的子拆分器)覆盖了元素的子集并近似于其报告的大小。 - 参见:
-
NONNULL
static final int NONNULL表示源保证遇到的元素不会是null
的特征值。 (例如,这适用于大多数并发集合、队列和映射。)- 参见:
-
IMMUTABLE
static final int IMMUTABLE表示元素源不能进行结构修改的特征值;也就是说,不能添加、替换或删除元素,因此在遍历期间不会发生此类更改。不报告IMMUTABLE
或CONCURRENT
的 Spliterator 应该有关于遍历期间检测到的结构干扰的记录策略(例如抛出ConcurrentModificationException
)。- 参见:
-
CONCURRENT
static final int CONCURRENT特征值表示元素源可以在没有外部同步的情况下由多个线程安全地并发修改(允许添加、替换和/或删除)。如果是这样,Spliterator 应该有一个关于遍历期间修改影响的文档化策略。顶级 Spliterator 不应同时报告
CONCURRENT
和SIZED
,因为有限大小(如果已知)可能会在遍历期间同时修改源时发生变化。这样的 Spliterator 是不一致的,并且不能保证使用该 Spliterator 进行任何计算。如果子拆分大小已知并且在遍历时未反映对源的添加或删除,则子拆分器可能会报告SIZED
。顶级 Spliterator 不应同时报告
CONCURRENT
和IMMUTABLE
,因为它们是互斥的。这样的 Spliterator 是不一致的,并且不能保证使用该 Spliterator 进行任何计算。如果在遍历时未反映对源的添加或删除,子拆分器可能会报告IMMUTABLE
。- API 注意:
- 大多数并发集合保持一致性策略,以保证在 Spliterator 构造时出现的元素的准确性,但可能不会反映后续的添加或删除。
- 参见:
-
SUBSIZED
static final int SUBSIZED特征值表示由trySplit()
产生的所有 Spliterators 都将是SIZED
和SUBSIZED
。 (这意味着所有子 Spliterator,无论是直接的还是间接的,都将是SIZED
。)不按照
SUBSIZED
的要求报告SIZED
的 Spliterator 是不一致的,并且不能保证使用该 Spliterator 的任何计算。- API 注意:
-
一些拆分器,例如近似平衡二叉树的顶级拆分器,将报告
SIZED
而不是SUBSIZED
,因为通常知道整棵树的大小而不是子树的确切大小。 - 参见:
-
-
方法详情
-
tryAdvance
如果存在剩余元素,则对其执行给定的操作,返回true
;否则返回false
。如果此 Spliterator 是ORDERED
,则按遇到顺序对下一个元素执行操作。操作抛出的异常被转发给调用者。如果操作抛出异常,拆分器的后续行为是未指定的。
- 参数:
action
- 动作- 返回:
false
如果进入此方法时不存在剩余元素,则为true
。- 抛出:
NullPointerException
- 如果指定的操作为空
-
forEachRemaining
在当前线程中按顺序对每个剩余元素执行给定操作,直到处理完所有元素或操作引发异常。如果此 Spliterator 是ORDERED
,则按遇到顺序执行操作。操作抛出的异常被转发给调用者。如果操作抛出异常,拆分器的后续行为是未指定的。
- 实现要求:
-
默认实现重复调用
tryAdvance(java.util.function.Consumer<? super T>)
直到它返回false
。应尽可能覆盖它。 - 参数:
action
- 动作- 抛出:
NullPointerException
- 如果指定的操作为空
-
trySplit
Spliterator <T > trySplit()如果此 spliterator 可以分区,则返回一个 Spliterator 重写元素,从该方法返回时,该 Spliterator 将不被此 Spliterator 重写。如果此 Spliterator 是
ORDERED
,则返回的 Spliterator 必须覆盖元素的严格前缀。除非此 Spliterator 涵盖无限数量的元素,否则对
trySplit()
的重复调用最终必须返回null
。返回非空值时:- 拆分前为
estimateSize()
报告的值,在拆分后必须大于或等于此和返回的 Spliterator 的estimateSize()
;和 - 如果此 Spliterator 是
SUBSIZED
,则拆分前此拆分器的estimateSize()
必须等于此拆分器的estimateSize()
和拆分后返回的 Spliterator 的总和。
该方法可能出于任何原因返回
null
,包括空、遍历开始后无法拆分、数据结构约束和效率考虑。- API 注意:
-
一个理想的
trySplit
方法有效地(无需遍历)将其元素精确地分成两半,从而允许平衡的并行计算。许多背离这一理想的做法仍然非常有效;例如,仅对一棵近似平衡的树进行近似分裂,或者对于叶子节点可能包含一个或两个元素的树,无法进一步分裂这些节点。然而,平衡的大偏差和/或过度低效的trySplit
机制通常会导致并行性能不佳。 - 返回:
-
a
Spliterator
覆盖元素的某些部分,或者null
如果不能拆分此拆分器
- 拆分前为
-
estimateSize
long estimateSize()返回forEachRemaining(java.util.function.Consumer<? super T>)
遍历将遇到的元素数量的估计值,或者如果无限、未知或计算成本太高则返回Long.MAX_VALUE
。如果此 Spliterator 是
SIZED
并且尚未被部分遍历或拆分,或者此 Spliterator 是SUBSIZED
并且尚未被部分遍历,则此估计必须是完整遍历将遇到的元素的准确计数。否则,这个估计可能是任意不准确的,但必须按照trySplit()
调用中指定的方式减少。- API 注意:
- 即使是不精确的估计通常也是有用的,而且计算成本低廉。例如,近似平衡二叉树的子拆分器可能会返回一个值,该值估计元素的数量是其父元素的一半;如果根 Spliterator 没有保持准确的计数,它可以将大小估计为其最大深度对应的 2 的幂。
- 返回:
-
估计大小,或者
Long.MAX_VALUE
如果无穷大、未知或计算成本太高。
-
getExactSizeIfKnown
default long getExactSizeIfKnown()- 实现要求:
-
如果 Spliterator 报告
SIZED
的特征,则默认实现返回estimateSize()
的结果,否则返回-1
。 - 返回:
-
确切的大小,如果知道,否则
-1
。
-
characteristics
int characteristics()返回此 Spliterator 及其元素的一组特征。结果表示为来自ORDERED
、DISTINCT
、SORTED
、SIZED
、NONNULL
、IMMUTABLE
、CONCURRENT
、SUBSIZED
的 ORed 值。在调用trySplit
之前或之间,在给定的拆分器上重复调用characteristics()
应该始终返回相同的结果。如果 Spliterator 报告一组不一致的特征(从单次调用返回的特征或跨多个调用返回的特征),则无法保证使用此 Spliterator 进行任何计算。
- API 注意:
-
给定分裂器在分裂前的特征可能与分裂后的特征不同。有关具体示例,请参见特征值
SIZED
、SUBSIZED
和CONCURRENT
。 - 返回:
- 特征的表示
-
hasCharacteristics
default boolean hasCharacteristics(int characteristics) 如果此 Spliterator 的characteristics()
包含所有给定特征,则返回true
。- 实现要求:
- 如果设置了给定特征的相应位,则默认实现返回 true。
- 参数:
characteristics
- 要检查的特征- 返回:
true
如果所有指定的特征都存在,否则false
-
getComparator
如果此 Spliterator 的源是Comparator
的SORTED
,则返回该Comparator
。如果来源是 自然秩序 中的SORTED
,则返回null
。否则,如果源不是SORTED
,则抛出IllegalStateException
。- 实现要求:
-
默认实现总是抛出
IllegalStateException
。 - 返回:
-
比较器,如果元素按自然顺序排序,则为
null
。 - 抛出:
IllegalStateException
- 如果拆分器不报告SORTED
的特征。
-