- 类型参数:
-
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仅用于单个批量计算。
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何时与元素源进行绑定;绑定后检测到元素源的结构干扰。 不是延迟绑定的Spliterator在首次遍历、首次分割或首次查询估计大小时绑定到元素源,而不是在创建Spliterator时绑定。不是延迟绑定的Spliterator在构建时或首次调用任何方法时绑定到元素源。在绑定之前对源进行的修改在遍历Spliterator时会反映出来。绑定后,Spliterator应在尽力的基础上,如果检测到结构干扰,则抛出ConcurrentModificationException
。执行此操作的Spliterator称为快速失败。Spliterator的批量遍历方法(forEachRemaining()
)可能会优化遍历,并在所有元素都被遍历后检查结构干扰,而不是逐个元素检查并立即失败。
Spliterator可以通过estimateSize()
方法提供剩余元素数量的估计。理想情况下,如特性SIZED
所反映的那样,此值应与成功遍历时遇到的元素数量完全对应。但即使不完全知道,估计值仍可能对正在执行的操作有用,例如帮助确定是更好地进一步分割还是顺序遍历剩余元素。
尽管在并行算法中显而易见地有用,但不要期望Spliterator是线程安全的;相反,使用Spliterator的并行算法的实现应确保Spliterator一次只被一个线程使用。通常通过串行线程封闭很容易实现,这通常是通过递归分解工作的典型并行算法的自然结果。调用trySplit()
的线程可以将返回的Spliterator交给另一个线程,然后该线程可能遍历或进一步分割该Spliterator。如果两个或更多线程同时在同一个Spliterator上操作,则分割和遍历的行为是未定义的。如果原始线程将Spliterator交给另一个线程进行处理,最好在使用tryAdvance()
消耗任何元素之前进行交接,因为某些保证(例如对于SIZED
Spliterator的estimateSize()
的准确性)仅在遍历开始之前有效。
为int
、long
和double
值提供了Spliterator
的原始子类型专门化。子类型的默认实现将原始值装箱为其对应的包装类的实例。这种装箱可能会削弱使用原始专门化所获得的任何性能优势。为避免装箱,应使用相应的基于原始类型的方法。例如,应优先使用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注释:
-
Spliterators(分割器)类似于
Iterator
,用于遍历源的元素。Spliterator
API旨在支持高效的并行遍历以及顺序遍历,通过支持分解以及单个元素迭代。此外,通过Spliterator
访问元素的协议旨在比Iterator
具有更小的每个元素开销,并避免了使用hasNext()
和next()
的分离方法所涉及的固有竞争。对于可变源,如果在
Spliterator
绑定到其数据源的时间和遍历结束之间对源进行结构干扰(添加、替换或删除元素),可能会发生任意和非确定性行为。例如,在使用java.util.stream
框架时,这种干扰将产生任意的、非确定性的结果。源的结构干扰可以通过以下方式进行管理(按照降低期望程度的大致顺序):
- 源不可被结构干扰。
例如,CopyOnWriteArrayList
的实例是一个不可变源。从源创建的Spliterator
报告了IMMUTABLE
的特征。 - 源管理并发修改。
例如,ConcurrentHashMap
的键集是一个并发源。从源创建的Spliterator
报告了CONCURRENT
的特征。 - 可变源提供延迟绑定和快速失败的Spliterator。
延迟绑定缩小了干扰可能影响计算的窗口;快速失败在遍历开始后检测到结构干扰并抛出ConcurrentModificationException
。例如,ArrayList
和JDK中的许多其他非并发Collection
类提供了延迟绑定、快速失败的spliterator。 - 可变源提供非延迟绑定但快速失败的Spliterator。
由于潜在干扰窗口较大,源增加了抛出ConcurrentModificationException
的可能性。 - 可变源提供延迟绑定和非快速失败的Spliterator。
由于未检测到干扰,源在遍历开始后可能产生任意的、非确定性的行为。 - 可变源提供非延迟绑定和非快速失败的Spliterator。
由于在构造后可能发生未检测到的干扰,源增加了任意的、非确定性行为的风险。
示例。 这里是一个类(除了用于说明之外并不是非常有用),它维护一个数组,其中实际数据保存在偶数位置,而不相关的标签数据保存在奇数位置。它的Spliterator忽略了标签。
class TaggedArray<T> { private final Object[] elements; // 在构造后不可变 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; // 当前索引,在分割或遍历时前进 private final int fence; // 最大索引之后 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 // 无法前进 return false; } public Spliterator<T> trySplit() { int lo = origin; // 将范围分成两半 int mid = ((lo + fence) >>> 1) & ~1; // 强制中点为偶数 if (lo < mid) { // 分割左半部分 origin = mid; // 重置此Spliterator的起始位置 return new TaggedArraySpliterator<>(array, lo, mid); } else // 太小无法分割 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
,则在操作原始子类型专用化时发生装箱时会报告诊断警告。 - 自Java版本:
- 1.8
- 参见:
-
Nested Class Summary
Modifier and TypeInterfaceDescriptionstatic interface
专门用于double
值的Spliterator。static interface
专门用于int
值的Spliterator。static interface
专门用于long
值的Spliterator。static interface
专门用于原始值的Spliterator。 -
Field Summary
Modifier and TypeFieldDescriptionstatic 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
-
Method Summary
Modifier and TypeMethodDescriptionint
返回此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覆盖。
-
Field Details
-
ORDERED
static final int ORDERED具有特征值,表示元素的遭遇顺序已定义。如果是这样,此Spliterator保证方法trySplit()
拆分元素的严格前缀,方法tryAdvance(java.util.function.Consumer<? super T>)
按前缀顺序逐个元素前进,forEachRemaining(java.util.function.Consumer<? super T>)
按遭遇顺序执行操作。如果
Collection
具有遭遇顺序,则相应的Collection.iterator()
记录了一个顺序。如果是这样,遭遇顺序与记录的顺序相同。否则,集合没有遭遇顺序。 -
DISTINCT
static final int DISTINCT- 参见:
-
SORTED
static final int SORTED具有特征值,表示遭遇顺序遵循定义的排序顺序。如果是这样,方法getComparator()
返回相关的比较器,如果所有元素都是Comparable
并且按它们的自然顺序排序,则返回null
。报告
SORTED
的Spliterator还必须报告ORDERED
。- API 注意:
-
JDK中实现
NavigableSet
或SortedSet
的Collection
类的Spliterator报告SORTED
。 - 参见:
-
SIZED
static final int SIZED具有特征值,表示在遍历或拆分之前从estimateSize()
返回的值表示一个有限大小,在没有结构源修改的情况下,表示完全遍历将遇到的元素数量的确切计数。- API 注意:
-
大多数覆盖
Collection
所有元素的Spliterator报告此特征。覆盖子集元素并近似其报告大小的子Spliterator,如HashSet
的Spliterator,不报告此特征。 - 参见:
-
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的任何计算。如果顶级Spliterator同时报告
CONCURRENT
和IMMUTABLE
,则它们是互斥的。这样的Spliterator是不一致的,不能保证使用该Spliterator的任何计算。如果子Spliterator在遍历时不反映源的添加或移除,则可以报告IMMUTABLE
。- API 注意:
- 大多数并发集合保持一致性策略,保证在Spliterator构造时存在的元素的准确性,但可能不反映后续的添加或移除。
- 参见:
-
SUBSIZED
static final int SUBSIZED具有特征值,表示从trySplit()
产生的所有Spliterators都将同时是SIZED
和SUBSIZED
。(这意味着所有子Spliterators,无论是直接的还是间接的,都将是SIZED
。)如果Spliterator不按照
SIZED
所需的方式报告SIZED
,则是不一致的,不能保证使用该Spliterator的任何计算。- API 注意:
-
一些Spliterator,例如近似平衡二叉树的顶级Spliterator,将报告
SIZED
但不报告SUBSIZED
,因为通常知道整个树的大小,但不知道子树的确切大小。 - 参见:
-
-
Method Details
-
tryAdvance
如果存在剩余元素:对其执行给定操作,返回true
;否则返回false
。如果此Spliterator是ORDERED
,则在遭遇顺序中执行操作。操作引发的异常将传递给调用者。如果操作引发异常,则Spliterator的后续行为是未指定的。
- 参数:
-
action
- 最多执行一次的操作 - 返回:
-
如果在进入此方法时不存在剩余元素,则返回
false
,否则返回true
。 - 抛出:
-
NullPointerException
- 如果指定的操作为null
-
forEachRemaining
为每个剩余元素依次在当前线程中执行给定操作,直到所有元素都已处理或操作引发异常。如果此Spliterator是ORDERED
,则按遭遇顺序执行操作。操作引发的异常将传递给调用者。如果操作引发异常,则Spliterator的后续行为是未指定的。
- 实现要求:
-
默认实现重复调用
tryAdvance(java.util.function.Consumer<? super T>)
,直到返回false
。应尽可能覆盖此方法。 - 参数:
-
action
- 操作 - 抛出:
-
NullPointerException
- 如果指定的操作为null
-
trySplit
Spliterator<T> trySplit()如果此Spliterator可以被分区,则返回一个覆盖元素的Spliterator,该Spliterator在从此方法返回后将不被此Spliterator覆盖。如果此Spliterator是
ORDERED
,则返回的Spliterator必须覆盖元素的严格前缀。除非此Spliterator覆盖无限数量的元素,否则重复调用
trySplit()
最终必须返回null
。在非null返回时:- 拆分前报告的
estimateSize()
值必须在拆分后大于或等于此Spliterator和返回的Spliterator的estimateSize()
值; - 如果此Spliterator是
SUBSIZED
,则拆分前此Spliterator的estimateSize()
值必须等于拆分后此Spliterator和返回的Spliterator的estimateSize()
值之和。
此方法可能出于任何原因返回
null
,包括空、在遍历开始后无法拆分、数据结构约束和效率考虑。- API 注意:
-
理想的
trySplit
方法会有效地(无需遍历)将其元素精确地一分为二,从而实现平衡的并行计算。许多与此理想的偏差仍然非常有效;例如,仅近似地拆分近似平衡的树,或者对于叶节点可能包含一个或两个元素的树,不进一步拆分这些节点。但是,平衡度的大幅偏差和/或过于低效的trySplit
机制通常会导致较差的并行性能。 - 返回:
-
覆盖一些元素的
Spliterator
,如果此Spliterator无法拆分,则返回null
- 拆分前报告的
-
estimateSize
long estimateSize()返回通过forEachRemaining(java.util.function.Consumer<? super T>)
遍历会遇到的元素数量的估计,如果是无限的、未知的或者计算成本太高,则返回Long.MAX_VALUE
。如果这个Spliterator是
SIZED
并且尚未部分遍历或分割,或者这个Spliterator是SUBSIZED
并且尚未部分遍历,那么这个估计必须是一个准确的元素计数,即完全遍历时会遇到的元素数量。否则,这个估计可能是任意不准确的,但必须在trySplit()
调用之间按照规定递减。- API注释:
- 即使是不精确的估计通常也是有用且廉价的计算。例如,一个大致平衡的二叉树的子Spliterator可能返回一个估计元素数量为其父级的一半;如果根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值。在对给定的spliterator进行多次调用trySplit
之前或之间,重复调用characteristics()
应始终返回相同的结果。如果一个Spliterator报告了不一致的特征集合(无论是从单次调用返回的还是跨多次调用返回的),则不能保证使用这个Spliterator进行任何计算。
- API注释:
-
在分割之前给定的Spliterator的特征可能与分割后的特征不同。具体示例请参见特征值
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
。 - 返回:
-
一个Comparator,如果元素按照自然顺序排序则返回
null
。 - 抛出:
-
IllegalStateException
- 如果Spliterator没有报告SORTED
特征。
-