模块 java.base
 java.util

接口 Spliterator<T>

类型参数:
T - 此 Spliterator 返回的元素类型
所有已知的子接口:
Spliterator.OfDouble , Spliterator.OfInt , Spliterator.OfLong , Spliterator.OfPrimitive<T,T_CONS,T_SPLITR>
所有已知的实现类:
Spliterators.AbstractDoubleSpliterator , Spliterators.AbstractIntSpliterator , Spliterators.AbstractLongSpliterator , Spliterators.AbstractSpliterator

public interface Spliterator<T>
用于遍历和划分源元素的对象。 Spliterator 覆盖的元素源可以是,例如,数组、Collection 、IO 通道或生成器函数。

Spliterator 可以单独遍历元素(tryAdvance() )或批量遍历元素(forEachRemaining() )。

Spliterator 也可以将它的一些元素(使用 trySplit() )划分为另一个 Spliterator,用于可能并行的操作。使用不能拆分的 Spliterator 的操作,或者以高度不平衡或低效的方式进行拆分,不太可能从并行性中受益。横向和分离式排气元件;每个 Spliterator 仅对单个批量计算有用。

Spliterator 还报告一组 characteristics() 的结构、来源和来自 ORDERED DISTINCT SORTED SIZED NONNULL IMMUTABLE CONCURRENT SUBSIZED 的元素。 Spliterator 客户端可以使用这些来控制、专门化或简化计算。例如,Collection 的 Spliterator 会报告 SIZEDSet 的 Spliterator 会报告 DISTINCTSortedSet 的 Spliterator 也会报告 SORTED。特征被报告为一个简单的联合位集。一些特征额外地限制了方法行为;例如,如果 ORDERED ,遍历方法必须符合其记录的顺序。将来可能会定义新的特征,因此实施者不应为未列出的值赋予含义。

不报告 IMMUTABLECONCURRENT 的 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
    特征值表示由 trySplit() 产生的所有 Spliterators 都将是 SIZED SUBSIZED
  • 方法总结

    修饰符和类型
    方法
    描述
    int
    返回此 Spliterator 及其元素的一组特征。
    long
    返回 forEachRemaining(java.util.function.Consumer<? super T>) 遍历将遇到的元素数量的估计值,或者如果无限、未知或计算成本太高则返回 Long.MAX_VALUE
    default void
    forEachRemaining(Consumer<? super T> action)
    在当前线程中按顺序对每个剩余元素执行给定操作,直到处理完所有元素或操作引发异常。
    default Comparator<? super T>
    如果此 Spliterator 的源是 Comparator SORTED ,则返回该 Comparator
    default long
    如果此 Spliterator 是 SIZED ,则返回 estimateSize() 的便捷方法,否则返回 -1
    default boolean
    hasCharacteristics(int characteristics)
    如果此 Spliterator 的 characteristics() 包含所有给定特征,则返回 true
    boolean
    tryAdvance(Consumer<? super T> action)
    如果存在剩余元素,则对其执行给定的操作,返回 true ;否则返回 false
    如果此 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 有一个遇到命令。如果是这样,遇到的顺序与记录的顺序相同。否则,集合没有遇到顺序。

      API 注意:
      任何 List 的遇到顺序保证是升序索引顺序。但是对于诸如 HashSet 之类的基于散列的集合,不能保证顺序。报告 ORDERED 的 Spliterator 的客户端应在非交换并行计算中保留排序约束。
      参见:
    • DISTINCT

      static final int DISTINCT
      特征值表示,对于每对遇到的元素 x, y!x.equals(y) 。例如,这适用于基于 Set 的 Spliterator。
      参见:
    • 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
      表示元素源不能进行结构修改的特征值;也就是说,不能添加、替换或删除元素,因此在遍历期间不会发生此类更改。不报告 IMMUTABLECONCURRENT 的 Spliterator 应该有关于遍历期间检测到的结构干扰的记录策略(例如抛出 ConcurrentModificationException )。
      参见:
    • CONCURRENT

      static final int CONCURRENT
      特征值表示元素源可以在没有外部同步的情况下由多个线程安全地并发修改(允许添加、替换和/或删除)。如果是这样,Spliterator 应该有一个关于遍历期间修改影响的文档化策略。

      顶级 Spliterator 不应同时报告 CONCURRENTSIZED ,因为有限大小(如果已知)可能会在遍历期间同时修改源时发生变化。这样的 Spliterator 是不一致的,并且不能保证使用该 Spliterator 进行任何计算。如果子拆分大小已知并且在遍历时未反映对源的添加或删除,则子拆分器可能会报告 SIZED

      顶级 Spliterator 不应同时报告 CONCURRENTIMMUTABLE ,因为它们是互斥的。这样的 Spliterator 是不一致的,并且不能保证使用该 Spliterator 进行任何计算。如果在遍历时未反映对源的添加或删除,子拆分器可能会报告 IMMUTABLE

      API 注意:
      大多数并发集合保持一致性策略,以保证在 Spliterator 构造时出现的元素的准确性,但可能不会反映后续的添加或删除。
      参见:
    • SUBSIZED

      static final int SUBSIZED
      特征值表示由 trySplit() 产生的所有 Spliterators 都将是 SIZED SUBSIZED 。 (这意味着所有子 Spliterator,无论是直接的还是间接的,都将是 SIZED 。)

      不按照 SUBSIZED 的要求报告 SIZED 的 Spliterator 是不一致的,并且不能保证使用该 Spliterator 的任何计算。

      API 注意:
      一些拆分器,例如近似平衡二叉树的顶级拆分器,将报告 SIZED 而不是 SUBSIZED ,因为通常知道整棵树的大小而不是子树的确切大小。
      参见:
  • 方法详情

    • tryAdvance

      boolean tryAdvance(Consumer <? super T > action)
      如果存在剩余元素,则对其执行给定的操作,返回 true ;否则返回 false 。如果此 Spliterator 是 ORDERED ,则按遇到顺序对下一个元素执行操作。操作抛出的异常被转发给调用者。

      如果操作抛出异常,拆分器的后续行为是未指定的。

      参数:
      action - 动作
      返回:
      false 如果进入此方法时不存在剩余元素,则为 true
      抛出:
      NullPointerException - 如果指定的操作为空
    • forEachRemaining

      default void forEachRemaining(Consumer <? super T > action)
      在当前线程中按顺序对每个剩余元素执行给定操作,直到处理完所有元素或操作引发异常。如果此 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
      实现要求:
      如果 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

      default Comparator <? super T > getComparator()
      如果此 Spliterator 的源是 Comparator SORTED ,则返回该 Comparator。如果来源是 自然秩序 中的 SORTED,则返回 null。否则,如果源不是 SORTED,则抛出 IllegalStateException
      实现要求:
      默认实现总是抛出 IllegalStateException
      返回:
      比较器,如果元素按自然顺序排序,则为 null
      抛出:
      IllegalStateException - 如果拆分器不报告 SORTED 的特征。