建网站需要哪些资质首都在线
Spliterator 接口概述
Spliterator (Splitable Iterator,可分割迭代器) 的核心设计目标是支持高效的并行遍历和元素分解。与传统的 Iterator相比,它提供了更强大的功能:
- 支持并行处理:通过
trySplit()方法,可以将数据源分割成多个部分,交由不同的线程独立处理。 - 更细粒度的遍历控制:
tryAdvance()方法一次处理一个元素,并返回是否还有更多元素,避免了Iterator中hasNext()和next()分离可能导致的竞态条件。 - 元素特性报告:通过
characteristics()方法,Spliterator可以报告其数据源的特性(如是否有序、元素是否唯一等),供 Stream API 进行优化。 - 大小估算:
estimateSize()方法提供对剩余元素数量的估算,有助于并行任务的负载均衡。
Spliterator 接口的主要方法和结构
我们来看一下 Spliterator<T> 接口定义的核心方法:
public interface Spliterator<T> {// ... (常量定义)boolean tryAdvance(Consumer<? super T> action);default void forEachRemaining(Consumer<? super T> action) {do { } while (tryAdvance(action));}Spliterator<T> trySplit();long estimateSize();default long getExactSizeIfKnown() {return (characteristics() & SIZED) == 0 ? -1L : estimateSize();}int characteristics();default boolean hasCharacteristics(int characteristics) {return (characteristics() & characteristics) == characteristics;}default Comparator<? super T> getComparator() {throw new IllegalStateException();}// ... (嵌套接口 OfPrimitive, OfInt, OfLong, OfDouble)
}
boolean tryAdvance(Consumer<? super T> action):- 作用: 如果存在剩余元素,则对下一个元素执行给定的
action,并返回true;否则返回false。 - 核心: 这是逐个元素遍历的核心方法。如果
Spliterator具有ORDERED特性,则按顺序处理元素。 - 实现: 具体类必须实现此方法。
- 作用: 如果存在剩余元素,则对下一个元素执行给定的
default void forEachRemaining(Consumer<? super T> action):- 作用: 对当前线程中所有剩余元素按顺序执行给定的
action,直到所有元素都被处理或action抛出异常。 - 默认实现:
java
它通过循环调用default void forEachRemaining(Consumer<? super T> action) {do { } while (tryAdvance(action)); }tryAdvance()来实现。实现类通常应覆盖此方法以提供更高效的批量处理。 - 实现: 这是一个
default方法,但强烈建议具体实现类覆盖它以获得更好的性能,例如直接遍历底层数据结构。
- 作用: 对当前线程中所有剩余元素按顺序执行给定的
Spliterator<T> trySplit():- 作用: 尝试将当前
Spliterator覆盖的元素集分割成两部分。如果成功分割,此方法返回一个新的Spliterator,覆盖元素集的前一部分,而当前Spliterator则覆盖剩余部分。如果无法分割(例如元素太少或数据结构不支持),则返回null。 - 核心: 这是实现并行处理的关键。Stream 框架会调用此方法来获取可以并行处理的数据块。
- 特性:
- 如果原
Spliterator是ORDERED的,返回的Spliterator必须覆盖一个严格的前缀。 - 重复调用最终必须返回
null(除非是无限流)。
- 如果原
- 实现: 具体类必须实现此方法。一个理想的
trySplit应该能高效地将元素大致均分。
- 作用: 尝试将当前
long estimateSize():- 作用: 估算通过
forEachRemaining遍历会遇到的剩余元素数量。如果元素数量是无限的、未知的或计算成本过高,则返回Long.MAX_VALUE。 - 特性:
- 如果
Spliterator具有SIZED特性且尚未部分遍历或分割,则此估算必须是精确的。 - 如果具有
SUBSIZED特性且尚未部分遍历,也必须是精确的。 - 否则,估算可能不准确,但必须在
trySplit调用后相应减少。
- 如果
- 实现: 具体类必须实现此方法。
- 作用: 估算通过
default long getExactSizeIfKnown():- 作用: 如果
Spliterator具有SIZED特性,则返回estimateSize()的结果;否则返回-1。 - 默认实现:
default long getExactSizeIfKnown() {return (characteristics() & SIZED) == 0 ? -1L : estimateSize(); } - 实现: 这是一个
default方法,通常不需要覆盖。
- 作用: 如果
int characteristics():- 作用: 返回一个表示此
Spliterator及其元素特性的整数(位掩码)。这些特性用于指导 Stream API 的优化。 - 特性常量:
ORDERED(0x00000010): 元素有确定的遇到顺序。DISTINCT(0x00000001): 每个遇到的元素都是唯一的 (!x.equals(y))。SORTED(0x00000004): 元素按定义的排序顺序排列。如果报告SORTED,也必须报告ORDERED。SIZED(0x00000040):estimateSize()返回的是精确的大小(在遍历或分割前)。NONNULL(0x00000100): 保证遇到的元素不会是null。IMMUTABLE(0x00000200): 元素源不能被结构性修改(添加、替换、删除)。CONCURRENT(0x00001000): 元素源可以安全地并发修改,而不会抛出ConcurrentModificationException,并且Spliterator能反映这些修改。SUBSIZED(0x00004000):trySplit()返回的Spliterator也都是SIZED和SUBSIZED的,并且分割前后的estimateSize()总和保持不变。SIZED 仅保证当前 Spliterator 的 estimateSize() 返回精确大小,但无法保证其子分割器(通过 trySplit() 获得)是否也能保持精确大小。例如:
树形结构:一个平衡二叉树的 Spliterator 可能知道总元素数量(SIZED),但分割后的子树大小可能无法精确计算(尤其是非完全二叉树)。
动态数据源:某些数据源可能在分割后丢失大小信息。
- 实现: 具体类必须实现此方法,并准确报告其数据源的特性。
- 作用: 返回一个表示此
default boolean hasCharacteristics(int characteristics):- 作用: 检查此
Spliterator是否包含所有给定的特性。 - 默认实现:
default boolean hasCharacteristics(int characteristics) {return (characteristics() & characteristics) == characteristics; } - 实现: 这是一个
default方法,通常不需要覆盖。
- 作用: 检查此
default Comparator<? super T> getComparator():- 作用: 如果源是按特定
Comparator排序的(即具有SORTED特性),则返回该Comparator。如果源是按自然顺序排序的,则返回null。如果源未排序,则抛出IllegalStateException。 - 默认实现:
default Comparator<? super T> getComparator() {throw new IllegalStateException(); } - 实现: 如果
Spliterator报告了SORTED特性,则应覆盖此方法以返回正确的比较器或null。
- 作用: 如果源是按特定
重要的概念(来自 Javadoc)
- 绑定 (Binding):
- 晚期绑定 (Late-binding):
Spliterator在首次遍历、首次分割或首次查询估算大小时才绑定到元素源。在此之前对源的修改会反映在遍历结果中。许多 JDK 集合(如ArrayList)提供晚期绑定的Spliterator。 - 非晚期绑定: 在构造时或首次调用任何方法时即绑定到元素源。
- 晚期绑定 (Late-binding):
- 结构性干扰 (Structural Interference): 在
Spliterator绑定到数据源之后、遍历结束之前,对源进行结构性修改(添加、删除、替换元素)。 - 快速失败 (Fail-fast): 在检测到结构性干扰后,尽最大努力抛出
ConcurrentModificationException。 - 线程安全:
Spliterator本身不要求是线程安全的。使用Spliterator的并行算法应确保一个Spliterator实例在同一时间只被一个线程操作。通常通过串行线程限制(serial thread-confinement)来实现,即一个线程调用trySplit()后,可以将返回的新Spliterator交给另一个线程处理。
原始类型特化
Spliterator 接口有针对原始数据类型的特化子接口,以避免自动装箱/拆箱带来的性能开销:
interface OfPrimitive<T, T_CONS, T_SPLITR extends Spliterator.OfPrimitive<T, T_CONS, T_SPLITR>>extends Spliterator<T>Spliterator.OfPrimitive<T, T_CONS, T_SPLITR extends Spliterator.OfPrimitive<T, T_CONS, T_SPLITR>>Spliterator.OfInt extends Spliterator.OfPrimitive<Integer, IntConsumer, Spliterator.OfInt>Spliterator.OfLong extends Spliterator.OfPrimitive<Long, LongConsumer, Spliterator.OfLong>Spliterator.OfDouble extends Spliterator.OfPrimitive<Double, DoubleConsumer, Spliterator.OfDouble>
这些特化接口定义了接受相应原始类型 Consumer (如 IntConsumer) 的 tryAdvance 和 forEachRemaining 方法。
Spliterator.OfPrimitive的范型是为了兼容接口
T:- 含义: 代表原始类型对应的包装类 (Wrapper Type)。
- 原因: Java 的泛型不支持直接使用原始类型(如
int,long)。因此,即使我们处理的是原始数据流,泛型签名中也必须使用对应的包装类。例如,对于int类型的流,T会是Integer。 - 作用: 尽管底层的操作可能是针对原始类型的,但
OfPrimitive仍然继承自Spliterator<T>,这意味着它需要符合Spliterator接口的一般契约,其中涉及到对象类型T。例如,Spliterator<T>中的tryAdvance(Consumer<? super T> action)方法接受的是一个处理对象T的Consumer。
T_CONS:- 含义: 代表针对特定原始类型的
Consumer接口。 - 原因: 为了避免在处理原始类型时发生自动装箱(autoboxing)和拆箱(unboxing)带来的性能开销,Java 8 引入了针对原始类型的函数式接口,如
IntConsumer,LongConsumer,DoubleConsumer。这个泛型参数T_CONS就是用来代表这些特定的原始类型消费者。 - 作用:
OfPrimitive接口会定义一些接受T_CONS类型参数的方法,例如boolean tryAdvance(T_CONS action);。这样,当处理原始类型流时,可以直接传递原始类型的消费者,避免了不必要的对象创建和转换。
- 含义: 代表针对特定原始类型的
T_SPLITR extends Spliterator.OfPrimitive<T, T_CONS, T_SPLITR>:- 含义: 代表
OfPrimitive接口自身的具体实现子类型。这是一种常见的递归泛型模式,有时也被称为 F-bounded polymorphism。 - 原因: 这种模式允许在父接口中定义返回其具体子类型的方法。
- 作用: 在
OfPrimitive接口中,有一个trySplit()方法,它需要返回一个与当前Spliterator类型相同的新的Spliterator。通过T_SPLITR这个泛型参数,可以确保trySplit()方法的返回类型是正确的具体原始类型Spliterator。例如,在OfInt中,trySplit()会返回OfInt。【因为T_SPLITR 是 Spliterator.OfPrimitive<T, T_CONS, T_SPLITR>子类,也就是extends Spliterator<T>,因此返回类型是严格子类型,满足重写条件】
- 含义: 代表
OfInt 优先使用 IntConsumer 来处理 int 值,以获得最佳性能。同时,为了保持与 Spliterator<Integer> 的兼容性,它也提供了接受 Consumer<Integer> 的方法,并在必要时进行适配
实现源码分析
由于 Spliterator 是一个接口,其“源码”主要是方法签名、default 方法的实现以及 Javadoc 中对行为契约的详细描述。
default 方法的实现:
forEachRemaining: 简单地循环调用tryAdvance。实现者应尽可能覆盖它以提高效率。getExactSizeIfKnown: 根据SIZED特性返回estimateSize()或-1。hasCharacteristics: 通过位运算检查特性。getComparator: 默认抛出异常,需要SORTED的Spliterator实现来覆盖。
Javadoc 中的示例实现 (TaggedArraySpliterator):
Javadoc 中提供了一个 TaggedArraySpliterator 的示例,这是一个很好的学习如何实现自定义 Spliterator 的起点。
// (部分代码,来自 Spliterator.java Javadoc)
static class TaggedArraySpliterator<T> implements Spliterator<T> {private final Object[] array;private int origin; // current index, advanced on split or traversalprivate final int fence; // one past the greatest indexTaggedArraySpliterator(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) // 跳过 tagaction.accept((T) array[origin]);}public boolean tryAdvance(Consumer<? super T> action) {if (origin < fence) {action.accept((T) array[origin]);origin += 2; // 跳过 tagreturn true;}else return false;}public Spliterator<T> trySplit() {int lo = origin;int mid = ((lo + fence) >>> 1) & ~1; // 确保 mid 是偶数索引if (lo < mid) { // 如果可以分割origin = mid; // 当前 spliterator 处理后半部分return new TaggedArraySpliterator<>(array, lo, mid); // 返回新的 spliterator 处理前半部分}else return null; // 太小无法分割}public long estimateSize() {return (long)((fence - origin) / 2); // 因为每两个元素有一个是数据}public int characteristics() {return ORDERED | SIZED | IMMUTABLE | SUBSIZED;}
}
这个例子清晰地展示了:
- 如何根据数据结构特点实现
tryAdvance和forEachRemaining。 trySplit如何将范围分割,并更新当前Spliterator的范围。estimateSize如何根据当前范围计算大小。characteristics如何报告数据源的特性。
总结
Spliterator 接口是 Java Stream API 实现高效顺序和并行处理的基石。它通过定义一套清晰的契约(包括遍历、分割、大小估算和特性报告),使得各种数据源都能以统一的方式接入 Stream 处理管道。理解 Spliterator 的设计和各个方法的语义对于深入理解 Stream API 的工作原理至关重要,也是实现自定义数据源以支持 Stream 操作的基础。
要深入理解其“实现”,除了阅读接口定义和 default 方法,更重要的是去阅读 JDK 中各种集合类(如 ArrayList, HashMap, LinkedList, ArrayDeque 等)是如何实现它们各自的 spliterator() 方法的,以及 java.util.Spliterators 工具类中提供的各种 Spliterator 实现。这些具体的实现会展示在不同数据结构下如何有效地实现分割和遍历逻辑。
Spliterators
Spliterators 类的主要功能是提供静态方法,用于操作或创建 Spliterator 及其原始类型特化(Spliterator.OfInt, Spliterator.OfLong, Spliterator.OfDouble)的实例。
其结构可以主要分为以下几个部分:
空 Spliterator 的工厂方法:
emptySpliterator(): 创建一个泛型为空的Spliterator<T>。emptyIntSpliterator(): 创建一个空的Spliterator.OfInt。emptyLongSpliterator(): 创建一个空的Spliterator.OfLong。emptyDoubleSpliterator(): 创建一个空的Spliterator.OfDouble。 这些空的 Spliterator 都报告Spliterator.SIZED和Spliterator.SUBSIZED特性,并且它们的trySplit()方法总是返回null。它们通常是通过内部类EmptySpliterator的静态常量实现的。
基于数组的 Spliterator 工厂方法: 这类方法用于从数组创建 Spliterator。它们通常接受一个数组、一个可选的起止索引范围以及一个
additionalCharacteristics参数,用于指定除了SIZED和SUBSIZED(这两个特性总是会被报告)之外的额外特性(例如IMMUTABLE,ORDERED)。- 针对
Object[](泛型):spliterator(Object[] array, int additionalCharacteristics)spliterator(Object[] array, int fromIndex, int toIndex, int additionalCharacteristics)
- 针对
int[]:spliterator(int[] array, int additionalCharacteristics)spliterator(int[] array, int fromIndex, int toIndex, int additionalCharacteristics)
- 针对
long[]、double[]类似
- 针对
基于迭代器 (Iterator) 的 Spliterator 工厂方法: 这类方法用于从
Collection或Iterator创建 Spliterator。- 从
Collection创建:spliterator(Collection<? extends T> c, int characteristics): 使用集合的迭代器和大小。
- 从
Iterator创建 (泛型和原始类型):spliterator(Iterator<? extends T> iterator, long size, int characteristics): 提供迭代器和已知大小。spliteratorUnknownSize(Iterator<? extends T> iterator, int characteristics): 提供迭代器,大小未知。- 类似地,为
PrimitiveIterator.OfInt,PrimitiveIterator.OfLong,PrimitiveIterator.OfDouble提供了带大小和未知大小的版本。 这些方法通常会实例化如IteratorSpliterator、IntIteratorSpliterator等内部或包级私有的 Spliterator 实现。
- 从
从 Spliterator 创建 Iterator 的工厂方法: 这类方法提供了将
Spliterator转换回Iterator的功能。iterator(Spliterator<? extends T> spliterator): 将泛型Spliterator转换为Iterator<T>。iterator(Spliterator.OfInt spliterator): 将Spliterator.OfInt转换为PrimitiveIterator.OfInt。iterator(Spliterator.OfLong spliterator): 将Spliterator.OfLong转换为PrimitiveIterator.OfLong。iterator(Spliterator.OfDouble spliterator): 将Spliterator.OfDouble转换为PrimitiveIterator.OfDouble。- 这些方法内部定义了一个名为
Adapter的局部内部类,该类同时实现了对应的Iterator接口和Consumer接口(或其原始类型特化版本),用于适配Spliterator的tryAdvance和forEachRemaining方法。
私有辅助方法:
checkFromToBounds(int arrayLength, int origin, int fence): 这是一个私有的静态方法,用于校验数组的起始索引(inclusive)和结束索引(exclusive)是否有效,如果无效则抛出ArrayIndexOutOfBoundsException。
总结: Spliterators 类是一个核心的工具类,它通过提供各种静态工厂方法,极大地简化了为不同数据源(如数组、集合、迭代器)创建不同类型(泛型、int、long、double)和不同特性组合的 Spliterator 对象的过程。
同时,它也提供了从 Spliterator 反向创建 Iterator 的能力。该类的设计体现了对不同场景和性能需求的考虑,例如区分已知大小和未知大小的数据源,以及允许用户指定额外的 Spliterator 特性。内部实现细节被很好地封装起来,对外提供清晰易用的API。
iterator
Spliterators.iterator(Spliterator<? extends T> spliterator) 这个静态方法,它用于从一个 Spliterator 创建一个 Iterator。
PrimitiveIterator.OfInt 等是类似的,不过对原始类型进行操作。
/*** Creates an {@code Iterator} from a {@code Spliterator}.** <p>Traversal of elements should be accomplished through the iterator.* The behaviour of traversal is undefined if the spliterator is operated* after the iterator is returned.** @param <T> Type of elements* @param spliterator The spliterator* @return An iterator* @throws NullPointerException if the given spliterator is {@code null}*/public static<T> Iterator<T> iterator(Spliterator<? extends T> spliterator) {// ... 实现 ...}
public static<T> Iterator<T> iterator(Spliterator<? extends T> spliterator):- 它接受一个
Spliterator<? extends T>类型的参数,返回一个Iterator<T>,这个迭代器将用于遍历Spliterator中的元素。
- 它接受一个
- 文档:
- 重要警告: "Traversal of elements should be accomplished through the iterator. The behaviour of traversal is undefined if the spliterator is operated after the iterator is returned."
- 这意味着一旦通过这个方法从
Spliterator创建了Iterator,就不应该再直接操作原来的Spliterator。否则,行为是未定义的,可能会导致迭代不一致或错误。这是因为Iterator的状态依赖于对底层Spliterator的操作。 - 如果传入的
spliterator为null,会抛出NullPointerException。
方法实现
Spliterators.java
Objects.requireNonNull(spliterator);class Adapter implements Iterator<T>, Consumer<T> {boolean valueReady = false;T nextElement;// ... Adapter 类的其他方法 ...}return new Adapter();
Objects.requireNonNull(spliterator);:- 这是一个防御性编程措施,确保传入的
spliterator不为null。如果为null,会立即抛出NullPointerException,而不是在后续操作中出现更难以追踪的错误。
- 这是一个防御性编程措施,确保传入的
class Adapter implements Iterator<T>, Consumer<T>:- 这里定义了一个局部内部类
Adapter。这个类是实现从Spliterator到Iterator转换的核心。 - 它同时实现了两个接口:
Iterator<T>: 这是它作为迭代器必须实现的接口,提供了hasNext()、next()和forEachRemaining()(Java 8 默认方法,这里也覆盖了) 方法。Consumer<T>: 这个接口的实现 (accept(T t)) 用于配合Spliterator.tryAdvance(Consumer<? super T> action)方法。当tryAdvance成功获取一个元素时,它会调用Adapter实例的accept方法,将元素传递过来。
- 这里定义了一个局部内部类
Adapter类的成员变量:boolean valueReady = false;:- 这是一个状态标志。当
Spliterator.tryAdvance()成功获取了一个元素并且该元素已经被Adapter的accept方法接收并存储在nextElement中时,valueReady会被设置为true。这表示下一个元素已经准备好,可以被next()方法返回。
- 这是一个状态标志。当
T nextElement;:- 用于临时存储从
Spliterator获取到的下一个元素。当valueReady为true时,nextElement持有这个待返回的元素。
- 用于临时存储从
Adapter 类的方法:
public void accept(T t):Spliterators.java
@Overridepublic void accept(T t) {valueReady = true;nextElement = t;}- 这是
Consumer<T>接口的实现。 - 当
spliterator.tryAdvance(this)被调用时,如果spliterator成功获取一个元素,它会将该元素传递给这个accept方法。 valueReady = true;: 将标志设置为true,表示有一个元素已经准备好了。nextElement = t;: 将获取到的元素t存储起来。
- 这是
public boolean hasNext():@Overridepublic boolean hasNext() {if (!valueReady)spliterator.tryAdvance(this);return valueReady;}- 这是
Iterator<T>接口的核心方法之一,用于检查是否还有更多元素。 if (!valueReady): 如果valueReady为false,意味着当前没有预先加载好的元素(可能是第一次调用hasNext(),或者上一个元素已经被next()消耗掉了)。spliterator.tryAdvance(this);: 在这种情况下,它会尝试从底层的spliterator获取下一个元素。注意,这里传递的Consumer是this,即Adapter实例本身。- 如果
tryAdvance成功,它会回调Adapter的accept方法,从而设置valueReady = true和nextElement。 return valueReady;: 返回valueReady的当前状态。如果tryAdvance成功了,valueReady会是true;如果tryAdvance失败(没有更多元素了),valueReady会保持false(或者在accept中没有被设置为true)。
- 这是
public T next():Spliterators.java
@Overridepublic T next() {if (!valueReady && !hasNext())throw new NoSuchElementException();else {valueReady = false;T t = nextElement;nextElement = null; // Help GCreturn t;}}- 这是
Iterator<T>接口的另一个核心方法,用于返回下一个元素。 if (!valueReady && !hasNext()): 这是一个双重检查。- 首先检查
!valueReady:如果当前没有预加载的元素。 - 然后调用
!hasNext():尝试加载下一个元素。如果hasNext()返回false(意味着tryAdvance失败,没有更多元素),则整个条件为真。 - 如果条件为真,说明确实没有下一个元素了,所以
throw new NoSuchElementException();,这符合Iterator.next()的规范。
- 首先检查
else: 如果valueReady为true(由上一次hasNext()调用或当前hasNext()调用成功设置),或者hasNext()在上面的条件中返回true。valueReady = false;: 将标志重置为false,因为即将返回的元素已经被消耗了。下一次调用hasNext()时需要重新尝试获取。T t = nextElement;: 获取存储的元素。nextElement = null;: 将nextElement设置为null,以帮助垃圾回收器回收该对象(如果它是最后一个引用)。return t;: 返回元素。
- 这是
public void forEachRemaining(Consumer<? super T> action):Spliterators.java
@Overridepublic void forEachRemaining(Consumer<? super T> action) {Objects.requireNonNull(action);if (valueReady) {valueReady = false;T t = nextElement;nextElement = null;action.accept(t);}spliterator.forEachRemaining(action);}- 这是
Iterator<T>接口的方法 (Java 8 中添加为默认方法,这里进行了覆盖以优化)。它对迭代器中的每个剩余元素执行给定操作。 Objects.requireNonNull(action);: 检查传入的action是否为null。if (valueReady): 如果有一个元素已经被hasNext()预加载并准备好了 (valueReady == true)。valueReady = false;: 消耗这个预加载的元素。T t = nextElement; nextElement = null;: 获取并清空预加载的元素。action.accept(t);: 对这个预加载的元素执行操作。
spliterator.forEachRemaining(action);: 然后,调用底层spliterator的forEachRemaining方法来处理所有再往后的剩余元素。这通常比通过循环调用hasNext()和next()更高效,因为Spliterator可能有更优化的内部批量处理方式。
- 这是
return new Adapter();:- 最后,该方法创建并返回一个新的
Adapter实例。这个Adapter对象就充当了基于原始Spliterator的Iterator。
- 最后,该方法创建并返回一个新的
总结与使用场景
这个 Spliterators.iterator() 方法提供了一种将 Spliterator 适配(Adapt)为传统 Iterator 的机制。
核心思想:
Iterator的hasNext()和next()是拉取(pull-based)模型:调用者请求下一个元素。Spliterator的tryAdvance(Consumer)是推送(push-based)或协作模型:你提供一个Consumer,Spliterator尝试获取一个元素并“推送”给这个Consumer。Adapter类通过内部状态 (valueReady,nextElement) 和同时实现Iterator与Consumer来桥接这两种模型。hasNext()尝试通过tryAdvance预取一个元素到nextElement,next()则消耗这个预取的元素。
什么时候会用到这个方法?
与期望
Iterator的旧版 API 交互:- 在 Java 8 引入 Stream 和 Spliterator 之前,Java 集合的迭代主要通过
Iterator接口。如果有一个Spliterator(可能来自一个 Stream,或者是一个自定义的Spliterator),但需要将其传递给一个只接受Iterator的旧方法或库,那么这个Spliterators.iterator()方法就非常有用。 - 例如,某个库函数
processItems(Iterator<String> items),而你有一个Stream<String> stream,你可以这样做:Stream<String> myStream = ... ; Iterator<String> iterator = Spliterators.iterator(myStream.spliterator()); legacyLibrary.processItems(iterator);
- 在 Java 8 引入 Stream 和 Spliterator 之前,Java 集合的迭代主要通过
需要
Iterator的特定行为或接口:- 虽然
Spliterator更强大(支持并行、更好的分割特性),但在某些情况下,你可能确实需要Iterator接口提供的简单hasNext()/next()模式,或者某个特定的Iterator子接口。
- 虽然
调试或简单的顺序遍历:
- 有时,为了调试或者进行非常简单的、明确的顺序元素处理,直接使用
Iterator的hasNext()/next()循环可能比构建一个完整的 Stream 流水线更直观或简单。如果你已经有了一个Spliterator,可以方便地转换为Iterator来进行这种操作。
- 有时,为了调试或者进行非常简单的、明确的顺序元素处理,直接使用
逐步迁移:
- 当一个代码库从旧的基于
Iterator的代码逐步迁移到使用 Stream API 时,这个方法可以作为过渡,允许新旧代码共存和交互。
- 当一个代码库从旧的基于
与 Java Stream 的关系:
- Java Stream API 在内部广泛使用
Spliterator作为其数据源的抽象。当你调用stream.iterator()时,实际上它内部可能就会使用类似Spliterators.iterator(this.spliterator())的逻辑(具体实现可能因 Stream 类型而异,但概念是相通的)。java
// Stream.java (概念性示例)default Iterator<T> iterator() {return Spliterators.iterator(spliterator()); } - 所以,当你从一个 Stream 获取
Iterator时,你间接或直接地使用了这种从Spliterator到Iterator的转换机制。
注意事项:
- 如文档所述,一旦创建了
Iterator,就不应再直接操作原始的Spliterator,因为Adapter的状态与Spliterator的状态是耦合的。 Iterator通常不支持并行处理,而Spliterator的核心优势之一就是其可分割性以支持并行。将Spliterator转换为Iterator通常意味着放弃了并行处理该数据源的能力(至少通过这个Iterator是这样)。
总的来说,Spliterators.iterator() 是一个实用的工具方法,它在 Spliterator 和传统的 Iterator 之间架起了一座桥梁,增强了 Java 集合框架和 Stream API 的互操作性。
ArraySpliterator
ArraySpliterator 是 java.util.Spliterators 类中的一个静态 final 内部类,它实现了 Spliterator<T> 接口。它的主要设计目标是为不可修改的 Object[] 数组提供一个高效的遍历和分割机制。
IntArraySpliterator 之类 也是类似的,不过对原始类型操作
Spliterators.java
// ... (其他 Spliterators 代码) ...// Array-based spliterators/*** A Spliterator designed for use by sources that traverse and split* elements maintained in an unmodifiable {@code Object[]} array.*/static final class ArraySpliterator<T> implements Spliterator<T> {// ... (成员变量和方法) ...}// ... (其他 Spliterators 代码) ...
成员变量
/*** The array, explicitly typed as Object[]. Unlike in some other* classes (see for example CR 6260652), we do not need to* screen arguments to ensure they are exactly of type Object[]* so long as no methods write into the array or serialize it,* which we ensure here by defining this class as final.*/private final Object[] array;private int index; // current index, modified on advance/splitprivate final int fence; // one past last indexprivate final int characteristics;private long estimatedSize; // if >= 0, the estimated size, to help to split evenly// if -1, exact size is known to be fence - index
private final Object[] array;- 作用: 这是
ArraySpliterator实际操作的底层数组。它被声明为final,意味着一旦在构造时赋值,就不能再指向其他数组对象。 - 类型:
Object[]。注释中提到,不需要严格检查传入的数组是否真的是Object[]类型(例如,可以是String[]),只要后续操作不向数组中写入或序列化它即可。由于ArraySpliterator被声明为final,可以保证其内部方法不会破坏这个约定。
- 作用: 这是
private int index;- 作用: 表示当前迭代器指向的数组元素的索引。当调用
tryAdvance成功处理一个元素后,或者当trySplit发生时,这个index会被修改。
- 作用: 表示当前迭代器指向的数组元素的索引。当调用
private final int fence;- 作用: 表示迭代的结束边界。它指的是最后一个要处理的元素的下一个索引。也就是说,迭代的范围是
[index, fence)。它被声明为final。
- 作用: 表示迭代的结束边界。它指的是最后一个要处理的元素的下一个索引。也就是说,迭代的范围是
private final int characteristics;- 作用: 存储了这个
Spliterator的特性。这些特性是通过位掩码表示的整数,例如Spliterator.SIZED(表示大小已知)、Spliterator.SUBSIZED(表示分裂后的子 Spliterator 大小也已知)、Spliterator.ORDERED(表示元素是有序的) 等。它被声明为final。
- 作用: 存储了这个
private long estimatedSize;- 作用: 用于存储对剩余元素数量的估计。
- 如果
estimatedSize的值为-1,表示Spliterator的大小是精确已知的,可以通过fence - index计算得出。这通常是初始创建或从具有精确大小的Spliterator分裂出来的情况。 - 如果
estimatedSize的值>= 0,表示这是一个估计的大小。这种情况通常发生在对一个非SIZED的Spliterator进行trySplit操作后,或者当Spliterator的大小不能精确确定时,分裂出的子Spliterator会有一个估计的大小,以帮助更均匀地进行并行处理。
- 如果
- 作用: 用于存储对剩余元素数量的估计。
构造函数
ArraySpliterator 提供了几个构造函数来满足不同的初始化需求。
/*** Creates a spliterator covering all of the given array.* Its size is known exactly and it is SIZED and SUBSIZED.* @param array the array, assumed to be unmodified during use* @param additionalCharacteristics Additional spliterator characteristics* of this spliterator's source or elements beyond {@code SIZED} and* {@code SUBSIZED} which are always reported*/public ArraySpliterator(Object[] array, int additionalCharacteristics) {this(array, 0, array.length, additionalCharacteristics);}
public ArraySpliterator(Object[] array, int additionalCharacteristics)- 作用: 创建一个覆盖整个给定数组的
Spliterator。 - 参数:
array: 要迭代的数组。additionalCharacteristics: 除了默认的SIZED和SUBSIZED之外,用户可以指定的其他特性。
- 行为: 它内部调用了更详细的构造函数
this(array, 0, array.length, additionalCharacteristics),将origin设为0,fence设为array.length。
- 作用: 创建一个覆盖整个给定数组的
/*** Creates a spliterator covering the given array and range.* Its size is known exactly and it is SIZED and SUBSIZED.* @param array the array, assumed to be unmodified during use* @param origin the least index (inclusive) to cover* @param fence one past the greatest index to cover* @param additionalCharacteristics Additional spliterator characteristics* of this spliterator's source or elements beyond {@code SIZED} and* {@code SUBSIZED} which are always reported*/public ArraySpliterator(Object[] array, int origin, int fence, int additionalCharacteristics) {this.array = array;this.index = origin;this.fence = fence;this.characteristics = additionalCharacteristics | Spliterator.SIZED | Spliterator.SUBSIZED;this.estimatedSize = -1;}
public ArraySpliterator(Object[] array, int origin, int fence, int additionalCharacteristics)- 作用: 创建一个覆盖数组指定范围
[origin, fence)的Spliterator。 - 参数:
array: 要迭代的数组。origin: 开始迭代的索引(包含)。fence: 结束迭代的索引(不包含)。additionalCharacteristics: 用户可以指定的其他特性。
- 行为:
- 初始化
array,index(设为origin),fence。 characteristics被设置为additionalCharacteristics与Spliterator.SIZED | Spliterator.SUBSIZED的按位或操作结果。这意味着通过这个构造函数创建的Spliterator总是具有SIZED和SUBSIZED特性,表示其大小是精确已知的,并且分裂后的子Spliterator大小也是精确已知的。estimatedSize被设置为-1,表明大小是精确的。
- 初始化
- 作用: 创建一个覆盖数组指定范围
/*** Creates a spliterator covering the given array and range but that is* not SIZED or SUBSIZED. This case occurs as a result of splitting another* spliterator that is not sized, so it's inappropriate for one of its* sub-spliterators to be sized.* @param array the array, assumed to be unmodified during use* @param origin the least index (inclusive) to cover* @param fence one past the greatest index to cover* @param characteristics characteristics of this spliterator's source; {@code SIZED} and* {@code SUBSIZED} are removed if present* @param estimatedSize the size estimate; should always be nonnegative*/private ArraySpliterator(Object[] array, int origin, int fence, int characteristics, long estimatedSize) {this.array = array;this.index = origin;this.fence = fence;this.characteristics = characteristics & ~(Spliterator.SIZED | Spliterator.SUBSIZED);this.estimatedSize = estimatedSize;}
private ArraySpliterator(Object[] array, int origin, int fence, int characteristics, long estimatedSize)- 作用: 这是一个私有构造函数,主要由
trySplit()方法内部调用,用于创建子Spliterator。 - 参数:
array,origin,fence: 与公共构造函数类似。characteristics: 从父Spliterator继承的特性。estimatedSize: 估计的大小,通常是非负数。
- 行为:
- 初始化
array,index,fence。 characteristics被设置为传入的characteristics与~(Spliterator.SIZED | Spliterator.SUBSIZED)的按位与操作结果。这意味着这个构造函数会移除SIZED和SUBSIZED特性(如果存在的话)。这是因为当一个Spliterator的大小不是精确已知时(例如,它的estimatedSize >= 0),它分裂出来的子Spliterator的大小通常也是估计的,因此不应该标记为SIZED或SUBSIZED。estimatedSize被设置为传入的estimatedSize。
- 初始化
- 作用: 这是一个私有构造函数,主要由
trySplit()
@Overridepublic Spliterator<T> trySplit() {int lo = index, mid = (lo + fence) >>> 1;if (lo >= mid) return null;if (estimatedSize == -1) { // Case 1: Exact size is knownreturn new ArraySpliterator<>(array, lo, index = mid, characteristics);}// Case 2: Size is estimatedlong prefixEstimatedSize = estimatedSize >>> 1;estimatedSize -= prefixEstimatedSize;return new ArraySpliterator<>(array, lo, index = mid, characteristics, prefixEstimatedSize);}
- 作用: 尝试将当前的
Spliterator分割成两个。如果成功,此方法返回一个新的Spliterator,它覆盖当前Spliterator范围的前半部分,而当前的Spliterator则修改其范围以覆盖后半部分。如果无法再分割(例如,剩余元素太少),则返回null。 - 逻辑:
int lo = index, mid = (lo + fence) >>> 1;:lo保存当前的起始索引index。
if (lo >= mid) return null;: 如果起始索引lo大于或等于中间点mid,意味着剩余的元素数量不足以进行分割(通常只剩一个元素或没有元素),则返回null。- 情况 1:
estimatedSize == -1(大小精确已知)return new ArraySpliterator<>(array, lo, index = mid, characteristics);- 创建一个新的
ArraySpliterator来处理前半部分[lo, mid)。 index = mid: 当前Spliterator的index(起始索引)被更新为mid,使其负责处理后半部分[mid, fence)。- 新的
Spliterator继承了原有的characteristics。由于父Spliterator的大小是精确的,新分裂出的子Spliterator的大小也是精确的,所以它会通过公共构造函数(间接或直接)被标记为SIZED和SUBSIZED(因为characteristics已经包含了这些)。
- 情况 2:
estimatedSize != -1(大小是估计的)long prefixEstimatedSize = estimatedSize >>> 1;: 估算前半部分的元素数量,通常是当前估计大小的一半。estimatedSize -= prefixEstimatedSize;: 更新当前Spliterator的估计大小,减去分配给新Spliterator的部分。return new ArraySpliterator<>(array, lo, index = mid, characteristics, prefixEstimatedSize);- 创建一个新的
ArraySpliterator来处理前半部分[lo, mid),使用私有构造函数。 - 当前
Spliterator的index更新为mid。 - 新的
Spliterator继承原有的characteristics(但私有构造函数会移除SIZED和SUBSIZED),并被赋予计算出的prefixEstimatedSize。
forEachRemaining(Consumer<? super T> action)
@SuppressWarnings("unchecked")@Overridepublic void forEachRemaining(Consumer<? super T> action) {Object[] a; int i, hi; // hoist accesses and checks from loopif (action == null)throw new NullPointerException();if ((a = array).length >= (hi = fence) &&(i = index) >= 0 && i < (index = hi)) {do { action.accept((T)a[i]); } while (++i < hi);}}
- 作用: 对
Spliterator中所有剩余的元素执行给定的action。 - 逻辑:
Object[] a; int i, hi;: 声明局部变量,并将对array,fence,index的访问提升到循环外部,这是一种微优化,可以减少循环内部的字段访问开销。if (action == null) throw new NullPointerException();: 检查action是否为null。if ((a = array).length >= (hi = fence) && (i = index) >= 0 && i < (index = hi)):- 这是一个关键的检查和赋值语句。
a = array: 将成员变量array赋值给局部变量a。hi = fence: 将成员变量fence赋值给局部变量hi。i = index: 将成员变量index赋值给局部变量i。a.length >= hi: 检查数组的实际长度是否至少与fence一样大,确保不会越界。i >= 0: 检查当前索引是否有效。i < (index = hi): 这是一个复合操作。首先检查当前索引i是否小于hi(即fence)。如果是,则将成员变量index更新为hi。这意味着在forEachRemaining执行后,Spliterator的index会移动到fence,表示所有元素都已被处理。
do { action.accept((T)a[i]); } while (++i < hi);: 如果上述条件都满足,则进入一个do-while循环。action.accept((T)a[i]): 对当前索引i处的元素执行action。这里有一个@SuppressWarnings("unchecked"),因为array是Object[]类型,需要强制转换为T。++i < hi: 递增索引i,并检查是否仍然小于hi。
tryAdvance(Consumer<? super T> action)
@Overridepublic boolean tryAdvance(Consumer<? super T> action) {if (action == null)throw new NullPointerException();if (index >= 0 && index < fence) {@SuppressWarnings("unchecked") T e = (T) array[index++];action.accept(e);return true;}return false;}
- 作用: 如果存在下一个元素,则对其执行给定的
action并返回true;否则返回false。 - 逻辑:
if (action == null) throw new NullPointerException();: 检查action是否为null。if (index >= 0 && index < fence): 检查当前index是否在有效范围[0, fence)内。- 如果条件为真,表示还有元素可以处理:
@SuppressWarnings("unchecked") T e = (T) array[index++];: 获取index处的元素,并将其赋值给e。然后,index自增,为下一次调用做准备。action.accept(e);: 对获取到的元素e执行action。return true;: 返回true表示成功处理了一个元素。
- 如果条件为真,表示还有元素可以处理:
- 如果
index不在有效范围内,则直接return false;,表示没有更多元素可供处理。
estimateSize()
@Overridepublic long estimateSize() {return estimatedSize >= 0 ? estimatedSize : (long)(fence - index);}
- 作用: 返回对剩余元素数量的估计值。
- 逻辑:
estimatedSize >= 0 ? estimatedSize : (long)(fence - index):- 如果
estimatedSize大于或等于0,说明它存储的是一个估计值,直接返回这个估计值。 - 否则(即
estimatedSize == -1),说明大小是精确已知的,通过(long)(fence - index)计算并返回剩余元素的精确数量。强制转换为long以匹配返回类型。
- 如果
getComparator()
@Overridepublic Comparator<? super T> getComparator() {if (hasCharacteristics(Spliterator.SORTED))return null;throw new IllegalStateException();}} // End of ArraySpliterator class
- 作用: 如果此
Spliterator的元素是SORTED(有序的)并且定义了排序比较器,则返回该比较器。如果元素是自然排序的,则返回null。如果元素不是SORTED的,则抛出IllegalStateException。 - 逻辑:
if (hasCharacteristics(Spliterator.SORTED)): 检查Spliterator是否具有SORTED特性。hasCharacteristics是Spliterator接口的默认方法,用于检查是否包含某个特定特性。- 如果为
true,则返回null。对于ArraySpliterator,它本身不强制或存储特定的比较器。如果数据源(数组)本身是有序的(例如,通过Arrays.sort()排序过),并且在创建Spliterator时指定了SORTED特性,那么这里返回null表示它是按自然顺序排序的,或者排序规则未由此Spliterator直接定义。
- 如果为
- 如果
Spliterator不具有SORTED特性,则throw new IllegalStateException();,因为在这种情况下调用getComparator()是没有意义的。
AbstractSpliterator<T>
AbstractSpliterator<T> 是 java.util.Spliterators 类中的一个公共静态抽象内部类,它实现了 Spliterator<T> 接口。它的主要目的是为创建自定义 Spliterator 提供一个便利的基类,特别是当源数据不容易或很难被高效地、均衡地分割以进行并行计算时。
AbstractIntSpliterator是类似的
它通过实现一个通用的 trySplit() 方法来提供有限的并行能力,这个 trySplit() 方法会尝试将元素分批(batch)处理。
类声明和文档注释
// ... (其他 Spliterators 代码) .../*** An abstract {@code Spliterator} that implements {@code trySplit} to* permit limited parallelism.** <p>An extending class need only* implement {@link #tryAdvance(java.util.function.Consumer) tryAdvance}.* The extending class should override* {@link #forEachRemaining(java.util.function.Consumer) forEachRemaining}* if it can provide a more performant implementation.** @apiNote* This class is a useful aid for creating a spliterator when it is not* possible or difficult to efficiently partition elements in a manner* allowing balanced parallel computation.** <p>An alternative to using this class, that also permits limited* parallelism, is to create a spliterator from an iterator* (see {@link #spliterator(Iterator, long, int)}. Depending on the* circumstances using an iterator may be easier or more convenient than* extending this class, such as when there is already an iterator* available to use.** @param <T> the type of elements returned by this Spliterator** @see #spliterator(Iterator, long, int)* @since 1.8*/public abstract static class AbstractSpliterator<T> implements Spliterator<T> {// ... (成员和方法) ...}
public abstract static class AbstractSpliterator<T> implements Spliterator<T>:implements Spliterator<T>: 实现了Spliterator接口,因此子类需要实现Spliterator接口中未被AbstractSpliterator实现的方法(主要是tryAdvance)。
- 文档注释:
- 核心功能: 提供了一个实现了
trySplit的Spliterator,允许有限的并行性。 - 子类责任: 继承此类通常只需要实现
tryAdvance(Consumer<? super T> action)方法。如果子类能提供更高性能的实现,也应该覆盖forEachRemaining(Consumer<? super T> action)。 - 使用场景 (API Note): 当无法或难以高效地、均衡地分割元素以进行并行计算时,此类非常有用。它提供了一种“尽力而为”的分割策略。
- 替代方案: 从
Iterator创建Spliterator(使用Spliterators.spliterator(Iterator, long, int)) 也是一种获得有限并行性的方法,有时可能更简单。
- 核心功能: 提供了一个实现了
成员
static final int BATCH_UNIT = 1 << 10; // batch array size increment (1024)static final int MAX_BATCH = 1 << 25; // max batch array size (33,554,432)
static final int BATCH_UNIT = 1 << 10;(即 1024)- 作用: 批处理大小的增量单位。在
trySplit时,每次分割出的批次大小会尝试在这个单位的基础上增加。
- 作用: 批处理大小的增量单位。在
static final int MAX_BATCH = 1 << 25;(即 33,554,432)- 作用: 批处理数组的最大大小。
trySplit创建的批次大小不会超过这个限制。
- 作用: 批处理数组的最大大小。
private final int characteristics;private long est; // size estimateprivate int batch; // batch size for splits
private final int characteristics;- 作用: 存储此
Spliterator的特性(例如ORDERED,SIZED等)。一旦在构造时设置,就不会改变。
- 作用: 存储此
private long est;- 作用: 存储对
Spliterator中剩余元素数量的估计值。这个值会在trySplit操作后被更新。
- 作用: 存储对
private int batch;- 作用: 用于
trySplit的当前批处理大小。这个值会动态调整,初始通常较小,然后逐渐增大,但不会超过MAX_BATCH。
- 作用: 用于
构造函数
/*** Creates a spliterator reporting the given estimated size and* additionalCharacteristics.** @param est the estimated size of this spliterator if known, otherwise* {@code Long.MAX_VALUE}.* @param additionalCharacteristics properties of this spliterator's* source or elements. If {@code SIZED} is reported then this* spliterator will additionally report {@code SUBSIZED}.*/protected AbstractSpliterator(long est, int additionalCharacteristics) {this.est = est;this.characteristics = ((additionalCharacteristics & Spliterator.SIZED) != 0)? additionalCharacteristics | Spliterator.SUBSIZED: additionalCharacteristics;}
protected AbstractSpliterator(long est, int additionalCharacteristics):protected: 构造函数是受保护的,意味着只有Spliterators包内的类或其子类可以调用。- 参数:
est: 初始的估计大小。如果大小未知,通常传入Long.MAX_VALUE。additionalCharacteristics: 用户指定的附加特性。
- 逻辑:
this.est = est;: 初始化估计大小。this.characteristics = ...: 初始化特性。这里有一个逻辑:如果传入的additionalCharacteristics中包含了Spliterator.SIZED特性,那么会自动添加Spliterator.SUBSIZED特性。这是因为如果一个Spliterator的大小是已知的,那么通过AbstractSpliterator的trySplit机制分割出来的子Spliterator(通常是ArraySpliterator)的大小也是已知的。
内部辅助类 HoldingConsumer<T>
static final class HoldingConsumer<T> implements Consumer<T> {Object value;@Overridepublic void accept(T value) {this.value = value;}}
- 作用: 这是一个非常简单的
Consumer实现,它的唯一目的是在tryAdvance调用时“持有”或捕获传递给它的单个元素。 Object value;: 用于存储通过accept方法接收到的值。注意这里用的是Object类型,因为泛型T在运行时会被擦除。public void accept(T value): 当tryAdvance(consumer)被调用时,如果成功获取一个元素,该元素会被传递到这个accept方法,并存储在this.value字段中。
这个类在 trySplit() 方法中用于从 tryAdvance() 中获取元素,以便将它们收集到一个批处理数组中。
trySplit() 方法
这是 AbstractSpliterator 的核心方法,它提供了一种通用的、基于批处理的分割策略。
@Overridepublic Spliterator<T> trySplit() {/** Split into arrays of arithmetically increasing batch* sizes. This will only improve parallel performance if* per-element Consumer actions are more costly than* transferring them into an array. The use of an* arithmetic progression in split sizes provides overhead* vs parallelism bounds that do not particularly favor or* penalize cases of lightweight vs heavyweight element* operations, across combinations of #elements vs #cores,* whether or not either are known. We generate* O(sqrt(#elements)) splits, allowing O(sqrt(#cores))* potential speedup.*/HoldingConsumer<T> holder = new HoldingConsumer<>();long s = est;if (s > 1 && tryAdvance(holder)) { // (1)int n = batch + BATCH_UNIT; // (2)if (n > s)n = (int) s;if (n > MAX_BATCH)n = MAX_BATCH; // (3)Object[] a = new Object[n]; // (4)int j = 0;do { a[j] = holder.value; } while (++j < n && tryAdvance(holder)); // (5)batch = j; // (6)if (est != Long.MAX_VALUE) { // (7)est -= j;return new ArraySpliterator<>(a, 0, j, characteristics);}// If original est was Long.MAX_VALUE, the new one is also effectively unbounded// but try to provide a somewhat reasonable estimate for the split part.return new ArraySpliterator<>(a, 0, j, characteristics, Long.MAX_VALUE / 2); // (8)}return null; // (9)}
注释解释:
- 分割策略是创建大小算术递增的批处理数组。
- 这种策略只有在处理单个元素的
Consumer操作比将元素转移到数组中的成本更高时,才能提高并行性能。 - 算术级数增长的分割大小提供了一种在开销和并行度之间的平衡,对于轻量级和重量级元素操作都有一定的适应性。
- 目标是产生大约 O(sqrt(元素数量)) 个分割,从而可能实现 O(sqrt(核心数量)) 的加速。
逻辑步骤:
HoldingConsumer<T> holder = new HoldingConsumer<>();: 创建一个HoldingConsumer实例来捕获由tryAdvance提供的元素。long s = est;: 获取当前的估计大小。if (s > 1 && tryAdvance(holder)): (1)- 检查条件:估计大小
s是否大于1(即至少有两个元素才可能分割),并且调用(子类实现的)tryAdvance(holder)是否成功获取了第一个元素。如果tryAdvance成功,获取到的元素会存储在holder.value中。 - 如果此条件不满足(例如,元素少于2个,或者
tryAdvance无法获取任何元素),则无法分割,直接跳到步骤 (9) 返回null。
- 检查条件:估计大小
int n = batch + BATCH_UNIT;: (2)- 计算下一次批处理的目标大小
n。它是在当前batch大小(初始可能为0)的基础上增加BATCH_UNIT(1024)。
- 计算下一次批处理的目标大小
if (n > s) n = (int) s;: 如果计算出的n超过了总的估计大小s,则将n限制为s。if (n > MAX_BATCH) n = MAX_BATCH;: (3)- 如果
n超过了MAX_BATCH,则将n限制为MAX_BATCH。
- 如果
Object[] a = new Object[n];: (4)- 创建一个大小为
n的Object数组a,用于存储这一批次的元素。
- 创建一个大小为
int j = 0; do { a[j] = holder.value; } while (++j < n && tryAdvance(holder));: (5)- 这是一个
do-while循环,用于填充批处理数组a。 a[j] = holder.value;: 将之前由tryAdvance(holder)(在步骤 (1) 或循环的上一轮)获取的元素存入数组a。++j < n && tryAdvance(holder): 递增计数器j。只要j仍然小于批处理大小n并且tryAdvance(holder)能够成功获取下一个元素,循环就继续。- 循环结束后,
j的值就是实际填充到数组a中的元素数量。
- 这是一个
batch = j;: (6)- 更新当前
Spliterator的batch成员变量为这次实际处理的元素数量j。这会影响下一次trySplit时的批处理大小计算。
- 更新当前
if (est != Long.MAX_VALUE): (7)- 如果原始的估计大小
est不是Long.MAX_VALUE(即大小是“已知”或有限估计的):est -= j;: 从当前Spliterator的估计大小中减去刚刚分割出去的元素数量j。return new ArraySpliterator<>(a, 0, j, characteristics);: 创建一个新的ArraySpliterator来包装刚刚填充的数组a(范围是从索引0到j)。这个新的ArraySpliterator继承了原始的characteristics。由于ArraySpliterator的构造函数(当大小精确时)会设置estimatedSize = -1并包含SIZED和SUBSIZED特性,所以这个返回的Spliterator是大小精确的。
- 如果原始的估计大小
return new ArraySpliterator<>(a, 0, j, characteristics, Long.MAX_VALUE / 2);: (8)- 如果原始的
est是Long.MAX_VALUE(表示大小未知或非常大):- 当前
Spliterator的est保持Long.MAX_VALUE。 - 为新创建的
ArraySpliterator提供一个估计大小Long.MAX_VALUE / 2。这是一个启发式的估计,因为原始大小未知。这里使用了ArraySpliterator的一个私有构造函数,该构造函数允许指定估计大小并会移除SIZED和SUBSIZED特性。
- 当前
- 如果原始的
return null;: (9)- 如果初始条件 (1) 不满足,则返回
null,表示无法分割。
- 如果初始条件 (1) 不满足,则返回
estimateSize() 方法
@Overridepublic long estimateSize() {return est;}
- 作用: 返回对剩余元素数量的估计。
- 逻辑: 直接返回成员变量
est的值。这个值在构造时初始化,并在每次成功的trySplit后(如果原始大小不是Long.MAX_VALUE)被更新。
总结与使用
AbstractSpliterator 的设计哲学是:子类只需要关注如何逐个提供元素(通过实现 tryAdvance),而将相对复杂的、通用的分割逻辑(trySplit)交给基类处理。
如何使用 AbstractSpliterator:
- 继承: 创建一个类继承自
AbstractSpliterator<T>。 - 构造函数: 在子类的构造函数中调用
super(estimatedSize, characteristics)来初始化估计大小和特性。- 如果源的大小已知,传入实际大小和包含
Spliterator.SIZED的特性。 - 如果源的大小未知或难以计算,传入
Long.MAX_VALUE作为estimatedSize。 - 根据源的特性(如是否有序
ORDERED,元素是否非空NONNULL等)设置characteristics。
- 如果源的大小已知,传入实际大小和包含
- 实现
tryAdvance(Consumer<? super T> action): 这是必须实现的核心方法。在此方法中,你需要:- 检查是否还有下一个元素。
- 如果有,获取下一个元素,并通过
action.accept(element)将其传递给消费者。 - 返回
true表示成功处理了一个元素。 - 如果没有更多元素,返回
false。
- 可选地覆盖
forEachRemaining(Consumer<? super T> action): 如果你的数据源可以更高效地批量处理剩余元素(而不是逐个通过tryAdvance),则可以覆盖此方法以提供优化实现。默认实现通常是循环调用tryAdvance。
示例场景 (概念性的):
假设你有一个自定义的数据结构,你想为它提供一个 Spliterator,但这个数据结构不容易直接进行高效的并行分割。
import java.util.Spliterators;
import java.util.Spliterator;
import java.util.function.Consumer;class MyCustomDataSource<T> {private java.util.Iterator<T> internalIterator; // 假设内部用迭代器private long estimatedSize;public MyCustomDataSource(java.util.Collection<T> data) {this.internalIterator = data.iterator();this.estimatedSize = data.size();}public Spliterator<T> spliterator() {return new MySpliterator<>(internalIterator, estimatedSize);}private static class MySpliterator<T> extends Spliterators.AbstractSpliterator<T> {private java.util.Iterator<T> it;protected MySpliterator(java.util.Iterator<T> iterator, long estSize) {// 假设元素是有序的,并且大小已知super(estSize, Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED);this.it = iterator;}@Overridepublic boolean tryAdvance(Consumer<? super T> action) {if (it.hasNext()) {action.accept(it.next());return true;}return false;}// 可以选择性地覆盖 forEachRemaining// @Override// public void forEachRemaining(Consumer<? super T> action) {// it.forEachRemaining(action);// }}
}
在这个例子中,MySpliterator 继承了 AbstractSpliterator,只需要提供 tryAdvance 的实现。trySplit 的逻辑由 AbstractSpliterator 提供,它会尝试从 MySpliterator 中批量拉取元素并创建 ArraySpliterator。
AbstractIntSpliterator、AbstractLongSpliterator 和 AbstractDoubleSpliterator 是针对基本数据类型 int、long 和 double 的类似抽象基类,它们也遵循相同的模式:子类实现 tryAdvance(针对相应的原始类型消费者,如 IntConsumer),基类提供 trySplit。这些原始类型的抽象类内部也会使用对应的 IntArraySpliterator 等来创建分割后的批次。
例如,在测试代码中: class IntSpliteratorFromArray extends Spliterators.AbstractIntSpliterator 这个测试类就是通过继承 AbstractIntSpliterator 并实现其 tryAdvance(IntConsumer action) 方法来创建一个用于 int[] 数组的 Spliterator。这展示了 AbstractSpliterator (及其原始类型特化版本) 的典型用法。
IteratorSpliterator
IteratorSpliterator<T> 是 java.util.Spliterators 类中的一个公共静态内部类,它实现了 Spliterator<T> 接口。其核心目的是将一个传统的 java.util.Iterator 适配(Adapt)成一个 Spliterator。
这在很多场景下都非常有用,特别是当你有一个基于 Iterator 的旧代码或数据源,但又想利用 Stream API 或其他需要 Spliterator 的现代 Java 特性时。
类声明和文档注释
Spliterators.java
// ... (其他 Spliterators 代码) .../*** A Spliterator using a given Iterator for element* operations. The spliterator implements {@code trySplit} to* permit limited parallelism.*/static class IteratorSpliterator<T> implements Spliterator<T> {// ... (成员和方法) ...}
static class IteratorSpliterator<T> implements Spliterator<T>:implements Spliterator<T>: 实现了Spliterator接口,因此它必须提供该接口所有方法的实现,如tryAdvance,trySplit,estimateSize,characteristics等。
- 文档注释:
- 核心功能: 这是一个使用给定的
Iterator进行元素操作的Spliterator。 - 并行性: 它实现了
trySplit方法,以允许有限的并行处理能力。这意味着即使原始的Iterator本身不支持并行,IteratorSpliterator也会尝试通过批处理的方式来分割任务。
- 核心功能: 这是一个使用给定的
成员
static final int BATCH_UNIT = 1 << 10; // batch array size increment (1024)static final int MAX_BATCH = 1 << 25; // max batch array size; (33,554,432)
这些常量与 AbstractSpliterator 中的定义和用途相同:
BATCH_UNIT: 批处理大小的增量单位,用于trySplit。MAX_BATCH: 批处理数组的最大大小,用于trySplit。
private final Collection<? extends T> collection; // null OKprivate Iterator<? extends T> it;private final int characteristics;private long est; // size estimateprivate int batch; // batch size for splits
private final Collection<? extends T> collection; // null OK- 作用: 可选地持有对原始
Collection的引用。如果IteratorSpliterator是通过一个Collection创建的,这个字段会被设置。这允许在需要时(例如,如果迭代器尚未初始化)从集合中重新获取迭代器和大小。如果直接通过Iterator创建,则此字段为null。
- 作用: 可选地持有对原始
private Iterator<? extends T> it;- 作用: 存储底层的
Iterator,实际的元素遍历将通过这个迭代器进行。它可以被延迟初始化(如果collection字段存在)。
- 作用: 存储底层的
private final int characteristics;- 作用: 存储此
Spliterator的特性。在构造时设置。
- 作用: 存储此
private long est;- 作用: 存储对
Spliterator中剩余元素数量的估计值。
- 作用: 存储对
private int batch;- 作用: 用于
trySplit的当前批处理大小,会动态调整。
- 作用: 用于
构造函数
IteratorSpliterator 提供了多个构造函数以适应不同的创建场景:
public IteratorSpliterator(Collection<? extends T> collection, int characteristics):public IteratorSpliterator(Collection<? extends T> collection, int characteristics) {this.collection = collection;this.it = null; // 迭代器延迟初始化this.characteristics = (characteristics & Spliterator.CONCURRENT) == 0? characteristics | Spliterator.SIZED | Spliterator.SUBSIZED: characteristics;}- 用途: 从一个
Collection创建Spliterator。 this.collection = collection;: 保存对集合的引用。this.it = null;: 迭代器it初始为null,它将在第一次需要时(如调用tryAdvance或trySplit)通过collection.iterator()获取。this.est也会在迭代器第一次被获取时通过collection.size()初始化。- 特性处理:
- 如果传入的
characteristics不包含Spliterator.CONCURRENT(意味着集合在迭代期间不会被并发修改),则会自动添加Spliterator.SIZED和Spliterator.SUBSIZED特性。这是因为可以安全地从集合获取大小,并且分割出的部分(通常是ArraySpliterator)也将是SIZED和SUBSIZED的。 - 如果包含
CONCURRENT,则直接使用传入的特性,因为集合大小可能在迭代期间变化,不能保证SIZED。
- 如果传入的
- 用途: 从一个
public IteratorSpliterator(Iterator<? extends T> iterator, long size, int characteristics):public IteratorSpliterator(Iterator<? extends T> iterator, long size, int characteristics) {this.collection = null;this.it = iterator;this.est = size;this.characteristics = (characteristics & Spliterator.CONCURRENT) == 0? characteristics | Spliterator.SIZED | Spliterator.SUBSIZED: characteristics;}- 用途: 从一个已有的
Iterator和已知的元素数量 (size) 创建Spliterator。 this.collection = null;: 没有关联的集合。this.it = iterator;: 直接使用传入的迭代器。this.est = size;: 使用传入的大小作为初始估计。- 特性处理与上一个构造函数类似,基于
CONCURRENT标志。
- 用途: 从一个已有的
public IteratorSpliterator(Iterator<? extends T> iterator, int characteristics):public IteratorSpliterator(Iterator<? extends T> iterator, int characteristics) {this.collection = null;this.it = iterator;this.est = Long.MAX_VALUE; // 大小未知this.characteristics = characteristics & ~(Spliterator.SIZED | Spliterator.SUBSIZED);}- 用途: 从一个已有的
Iterator创建Spliterator,但源的大小未知。 this.collection = null;this.it = iterator;this.est = Long.MAX_VALUE;: 因为大小未知,所以估计大小设为最大长整型。- 特性处理: 从传入的
characteristics中移除SIZED和SUBSIZED特性,因为大小是未知的。
- 用途: 从一个已有的
方法实现
private void init()(非实际方法,但逻辑分散在各方法中): 许多方法(如trySplit,forEachRemaining,tryAdvance,estimateSize)在开始时都有类似的逻辑来确保迭代器it和估计大小est被初始化(如果它们尚未初始化且collection存在):// 概念性逻辑 if (it == null) {it = collection.iterator();est = (long) collection.size(); }
public long estimateSize():@Overridepublic long estimateSize() {if (it == null) { // 如果迭代器未初始化,从集合获取大小it = collection.iterator();return est = (long)collection.size();}return est; // 返回当前估计大小}- 如果迭代器
it尚未初始化(意味着是通过collection构造的),则初始化它并从collection.size()获取初始估计大小。 - 返回当前的估计大小
est。
- 如果迭代器
public Comparator<? super T> getComparator():@Overridepublic Comparator<? super T> getComparator() {if (hasCharacteristics(Spliterator.SORTED))return null; // 如果是 SORTED 但没有具体的 Comparator,返回 nullthrow new IllegalStateException();}- 如果
Spliterator具有SORTED特性,此方法应该返回用于排序的Comparator,或者如果元素是自然排序的,则返回null。 IteratorSpliterator的这个实现比较简单:如果它被标记为SORTED,它就假设是自然排序或排序比较器未知,因此返回null。- 如果未标记为
SORTED,调用此方法会抛出IllegalStateException,这符合Spliterator.getComparator()的规范。
- 如果
Spliterator<T> trySplit()
@Overridepublic Spliterator<T> trySplit() {Iterator<? extends T> i;long s;if ((i = it) == null) { // (1) 确保迭代器已初始化i = it = collection.iterator();s = est = (long) collection.size();}elses = est;if (s > 1 && i.hasNext()) { // (2) 检查是否可以分割int n = batch + BATCH_UNIT;if (n > s)n = (int) s;if (n > MAX_BATCH)n = MAX_BATCH;Object[] a = new Object[n]; // (3) 创建批处理数组int j = 0;do { a[j] = i.next(); } while (++j < n && i.hasNext()); // (4) 从迭代器填充数组batch = j; // (5) 更新批处理大小if (est != Long.MAX_VALUE) { // (6) 更新估计大小并返回 ArraySpliteratorest -= j;return new ArraySpliterator<>(a, 0, j, characteristics);}// (7) 如果原始大小未知,返回带有启发式估计大小的 ArraySpliteratorreturn new ArraySpliterator<>(a, 0, j, characteristics, Long.MAX_VALUE / 2);}return null; // (8) 无法分割}
- 逻辑与
AbstractSpliterator.trySplit()非常相似:- (1) 确保迭代器
it和估计大小est已初始化(如果它们是通过collection构造的且尚未初始化)。 - (2) 检查估计大小
s是否大于1,并且迭代器i是否还有下一个元素 (i.hasNext())。 - (3) 计算批处理大小
n(基于batch,BATCH_UNIT,s,MAX_BATCH),并创建批处理数组a。 - (4) 使用
do { a[j] = i.next(); } while (...)循环从迭代器i中获取元素并填充到数组a中,直到数组满或迭代器耗尽。 - (5) 更新成员变量
batch为实际填充的元素数量j。 - (6) 如果原始估计大小
est不是Long.MAX_VALUE,则从中减去j,并创建一个新的ArraySpliterator来包装数组a。 - (7) 如果原始
est是Long.MAX_VALUE,则创建一个ArraySpliterator,并为其提供一个启发式的估计大小 (Long.MAX_VALUE / 2)。 - (8) 如果不满足分割条件 (2),则返回
null。
- (1) 确保迭代器
- 关键区别:
AbstractSpliterator依赖子类实现的tryAdvance(Consumer)来获取元素,而IteratorSpliterator直接使用其内部的Iterator.next()来获取元素。
boolean tryAdvance(Consumer<? super T> action):
@Overridepublic boolean tryAdvance(Consumer<? super T> action) {if (action == null) throw new NullPointerException();if (it == null) { // 确保迭代器已初始化it = collection.iterator();est = (long) collection.size();}if (it.hasNext()) {action.accept(it.next());return true;}return false;}
- 检查
action是否为null。 - 确保迭代器
it已初始化。 - 如果
it.hasNext()为true,则调用it.next()获取元素,并通过action.accept()传递给消费者,然后返回true。 - 否则,返回
false。
forEachRemaining(Consumer<? super T> action):
@Overridepublic void forEachRemaining(Consumer<? super T> action) {if (action == null) throw new NullPointerException();Iterator<? extends T> i;if ((i = it) == null) { // 确保迭代器已初始化i = it = collection.iterator();est = (long)collection.size();}i.forEachRemaining(action); // 委托给 Iterator 的 forEachRemaining}
- 检查
action是否为null。 - 确保迭代器
it已初始化。 - 直接调用底层迭代器
i的forEachRemaining(action)方法。这通常比循环调用tryAdvance更高效。
为什么需要 IteratorSpliterator?
桥接旧 API 与新 API:
- Java 8 引入了 Stream API 和
Spliterator,它们提供了更强大和灵活的数据处理方式,特别是对于并行处理。然而,大量的现有代码和库仍然使用Iterator。 IteratorSpliterator充当了一个桥梁,允许你轻松地将基于Iterator的数据源包装成Spliterator,从而可以在 Stream API 中使用它们。- 例如,如果你有一个返回
Iterator的旧方法getLegacyDataIterator(),你可以这样做:java
或者,如果迭代器来自一个集合:Iterator<String> legacyIterator = getLegacyDataIterator(); Spliterator<String> spliterator = Spliterators.spliteratorUnknownSize(legacyIterator, 0); // 假设大小未知,无特定特性 Stream<String> stream = StreamSupport.stream(spliterator, false); stream.forEach(System.out::println);
实际上,Collection<String> myCollection = ... ; Spliterator<String> spliterator = Spliterators.spliterator(myCollection, 0); // 假设无特定特性,会自动添加 SIZED Stream<String> stream = StreamSupport.stream(spliterator, false);Collection.spliterator()的默认实现通常就是返回一个IteratorSpliterator(或类似的基于迭代器的Spliterator)。
- Java 8 引入了 Stream API 和
为
Iterator提供有限的并行能力:Iterator本身是严格顺序的,不直接支持并行处理。IteratorSpliterator通过其trySplit()方法实现了一种“尽力而为”的并行化策略。它尝试从迭代器中批量读取元素到一个数组中,然后为这个数组创建一个ArraySpliterator。这个ArraySpliterator是可以被并行处理的。- 虽然这种并行性不如那些原生支持高效分割的数据源(如数组或某些并发集合的
Spliterator),但它仍然可以在某些情况下提供性能改进,特别是当处理每个元素的操作比较耗时的时候。
统一数据源抽象:
Spliterator是 Stream API 的核心数据源抽象。通过将Iterator转换为Spliterator,所有数据源都可以通过统一的Spliterator接口进行操作,简化了 Stream API 内部的实现。
逐步迁移:
- 当项目从旧的迭代方式迁移到 Stream API 时,
IteratorSpliterator使得可以逐步进行,而不需要立即重写所有数据访问逻辑。可以先将现有的Iterator包装起来,然后在后续重构中考虑是否可以提供更高效的、原生的Spliterator实现。
- 当项目从旧的迭代方式迁移到 Stream API 时,
与 AbstractSpliterator 的关系:
AbstractSpliterator是一个抽象基类,用于帮助用户创建自定义的Spliterator,它要求子类实现tryAdvance。IteratorSpliterator是一个具体的Spliterator实现,它不继承自AbstractSpliterator。它直接实现了Spliterator接口,并使用内部持有的Iterator来实现tryAdvance和trySplit的元素获取逻辑。- 两者都实现了基于批处理的
trySplit逻辑,但AbstractSpliterator是为了让用户提供元素,而IteratorSpliterator是为了包装一个已有的元素提供者 (Iterator)。
总而言之,IteratorSpliterator 是 Java 集合框架和 Stream API 之间一个非常重要的粘合剂,它使得基于 Iterator 的传统数据源能够无缝地集成到现代的、基于 Spliterator 和 Stream 的编程模型中,并提供了一定程度的并行处理能力。
trySplit的分割容量
IteratorSpliterator 和 AbstractSpliterator 的 trySplit 方法中批处理(batch)大小的选择是设计成尝试逐步增大的。
n = batch + BATCH_UNIT;
然后 batch = n
为什么采用这种递增的批处理大小策略?
这种算术级数增加批处理大小的策略是一种启发式方法,旨在平衡以下几个方面:
- 并行开销与粒度:
- 如果每次分割的批次太小,会导致产生大量的并行任务,而创建和管理这些小任务的开销可能会超过并行带来的好处。
- 如果批次太大,可能会导致任务粒度过粗,无法充分利用多核处理器的并行能力,或者在任务分配不均时导致某些核心空闲。
- 适应性:
- 初始批次较小:对于元素总数较少的数据源,可以避免一次分割就取走大部分数据。
- 逐渐增大批次:对于元素总数较多的数据源,随着分割的进行,尝试获取更大的数据块,以期在后续的并行处理中每个任务能有足够的工作量,同时减少分割操作的总次数。
- 减少分割次数:
- 通过逐步增大大块,可以期望以
O(sqrt(总元素数))级别的分割次数来处理整个数据源。这是一种在分割开销和并行任务数量之间寻求平衡的常见策略。
- 通过逐步增大大块,可以期望以
- 对元素处理成本的普适性:
- 文档注释中提到,这种算术级数增长的分割大小“不会特别偏袒或惩罚轻量级与重量级元素操作的场景”。这意味着无论处理每个元素的成本高低,这种策略都能提供一个相对合理的开销与并行度的平衡。
总结一下:
batch 成员变量记录了上一次从当前 Spliterator 分割出去的元素数量。每次调用 trySplit 时,会尝试在上一次分割数量的基础上增加 BATCH_UNIT 来确定新的目标分割数量 n。然后,这个 n 会受到剩余元素总量和最大批次大小的限制。实际分割后,batch 成员变量会更新为这次实际分割的数量。这个机制导致了在对同一个 Spliterator 进行连续的 trySplit 操作时,尝试分割的批次大小会呈算术级数增长,直到达到上限或者元素耗尽。
