美文网首页
从底层重学 Java 之 Stream 分离器及 Sink

从底层重学 Java 之 Stream 分离器及 Sink

作者: you的日常 | 来源:发表于2022-01-07 12:06 被阅读0次

1. 简介

从底层,从原理,我们来重学一次 Java。Stream 是 JDK 8 中新引入的,方便了数据列表的过滤、投影、遍历等各种处理,他的源码及实现是怎样的呢?

本文秉承所有结论尽量从源码中来,没有源码的尽量标明出处。相关源码会附着在文章中,读本文即可,不用再自行查找源码及资料学习,方便大家充分利用路上的碎片时间。

分离器(Spliterator)用于遍历和切分集合元素,终端操作遍历直接就是调用分离器的遍历方法,并行处理是用分离器进行并行任务切分的。例如:Arrays.stream 内部返回的 StreamSupport.stream(spliterator(array, startInclusive, endExclusive), false),第一个参数就是分离器(Spliterator)。

Sink 是在 Stream 的终端操作(如 forEach)进行处理时,·AbstractPipeline.wrapSink· 方法向上遍历管道阶段时,中间管道阶段 opWrapSink 方法实现返回的接口。过滤(filter)、投影(map)、排序(sorted)等中间操作的核心代码均写在 Sink 的接口实现中,可见 Sink 的重要性。

本篇 分析了分离器(Spliterator)相关源码,以及 Sink 源码,帮助大家深入理解和学习 JDK 源码。

适用人群:有一些 Java 基础的人群。

2. Spliterators

用于操作或创建 Spliterator(分离器)及其原语专门化的实例的静态类和方法 Spliterator.OfInt、Spliterator.OfLong 和 Spliterator.OfDouble。JDK 1.8 之后才有。

public final class Spliterators

2.1 spliterator(Collection c, int characteristics)

基于迭代器的拆分器。使用给定集合的java.util.Collection.iterator() 创建 Spliterator 作为元素的源,并报告其 java.util.Collection.size() 作为初始大小。

拆分器是后期绑定,继承集合迭代器的 fail-fast(快速失败)属性,并实现 trySplit 以允许有限的并行性。

    public static <T> Spliterator<T> spliterator(Collection<? extends T> c,
                                                 int characteristics) {
        return new IteratorSpliterator<>(Objects.requireNonNull(c),
                                         characteristics);
    }

2.2 Objects.requireNonNull(T obj)

检查指定的对象引用是否不是 null。该方法主要用于在方法和构造函数中进行参数验证。

    public static <T> T requireNonNull(T obj) {
        if (obj == null)
            throw new NullPointerException();
        return obj;
    }

使用示例如下所示:

public Foo(Bar bar) {
     this.bar = Objects.requireNonNull(bar);
}

2.3 Spliterators.spliteratorUnknownSize(Iterator iterator,int characteristics)

使用给定 Iterator 作为元素源创建一个 Spliterator,没有初始大小估计。

分离器不是后绑定的,它继承了迭代器的快速失败属性,并实现 trySplit 以允许有限的并行性。

元素的遍历应通过分离器完成。如果在返回拆分器之后对迭代器进行操作,则拆分和遍历的行为是不确定的。


     /*
     * @param <T> 元素类型
     * @param iterator 
     源的迭代器
     * @param characteristics 此分离器的源或元素的特征(SIZED 和 SUBSIZED,如果提供的话,将被忽略并且不会报告。)
     * @return 来自迭代器的分离器
     * @throws NullPointerException 如果给定的迭代器是 null
     */
    public static <T> Spliterator<T> spliteratorUnknownSize(Iterator<? extends T> iterator,
                                                            int characteristics) {
        return new IteratorSpliterator<>(Objects.requireNonNull(iterator), characteristics);
    }

3. IteratorSpliterator

使用给定迭代器进行元素操作的拆分器。spliterator(拆分器)实现 trySplit 以允许有限的并行性。

3.1 类定义

    static class IteratorSpliterator<T> implements Spliterator<T> {

静态类,实现 Spliterator<T> 接口。

3.2 常量

        static final int BATCH_UNIT = 1 << 10;  // batch array size increment   
            //批处理数组大小增量
        static final int MAX_BATCH = 1 << 25;  // max batch array size; 
            //最大批处理数组大小
        private final Collection<? extends T> collection; // null OK 集合,可以为空
        private Iterator<? extends T> it;   // 迭代器
        private final int characteristics;  //特征(一些特征标记,通过位运算记录和判断)
        private long est;             // size estimate  估算大小
        private int batch;            // batch size for splits  拆分的批量大小

3.3 构造函数

3.3.1.IteratorSpliterator(Collection collection, int characteristics)

创建分离器,使用给定集合的 java.util.Collection.iterator()进行遍历,并报告其 java.util.Collection.size()作为初始大小。

        /**
         * @param c 集合
         * @param 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;
        }

3.3.2 IteratorSpliterator(Iterator iterator, long size, int characteristics)

使用给定的遍历迭代器创建分离器,并报告给定的初始大小和特征。

    /**
     * @param iterator 源的迭代器
     * @param size 源内元素个数
     * @param 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;
    }

3.3.3 IteratorSpliterator(Iterator iterator, int characteristics)

使用给定的遍历迭代器创建拆分器,并报告给定的初始大小和特征。

        /**
         * @param iterator 源的迭代器
         * @param 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);
        }

4. Spliterator

分离器(Spliterator)用于遍历和切分集合元素,终端操作遍历直接就是调用分离器的遍历方法,并行处理是用分离器进行并行任务切分的。例如:Arrays.stream 内部返回的 StreamSupport.stream(spliterator(array, startInclusive, endExclusive), false),第一个参数就是分离器(Spliterator)。

4.1 类的定义

用于遍历和划分源元素的对象。

分离器覆盖的元素的来源可以是例如:数组、Collection、IO 通道或生成器函数。

分离器可以单独遍历元素(tryAdvance())或顺序遍历元素(forEachRemaining())。

Spliterator(分离器)还可以将其某些元素(使用 trySplit()) 划分为另一个 Spliterator(分离器),以用于可能并行的操作。操作使用 Spliterator 操作无法拆分,或者以高度不平衡或低效的方式拆分,因此不太可能从并行性中受益。遍历和拆分排气元素; 每个 Spliterator 仅用于单个批量计算。

Spliterator 还可以从 ORDEREDDISTINCTSORTEDSIZEDNONNULLIMMUTABLECONCURRENTSUBSIZED 报告其结构、源和元素的一组 characteristics()。这些可能被 Spliterator 客户端用来控制,专门化或简化计算。例如,Spliterator 为 Collection 将报告 SIZED,Spliterator 为 Set 将报告 DISTINCT,Spliterator 为 SortedSet 将报告 SORTED。特性报告为简单的联合位集。

一些特征还限制了方法的行为。例如:如果 ORDERED,遍历方法必须符合其记录的顺序。将来可能会定义新的特性,因此实现者不应为未列出的值分配含义。

一个未报告 IMMUTABLECONCURRENT 的分离器应要求记录影响原则:

  • 分离器何时绑定到元素源、以及元素源在构建后发现结构干涉。
  • 后期绑定分离器在第一次遍历、第一次拆分或第一次查询估计大小时绑定到元素源,而不是在创建拆分器时。
  • 非后期绑定分离器在构造或第一次调用任何方法时绑定到元素源。
  • 在绑定之前对源所做的修改会在遍历分离器时反映出来。
  • 在绑定之后,如果检测到结构干涉分离器应该尽最大努力抛出 ConcurrentModificationException 异常。分离器执行的这个操作称为快速失败。
  • 分离器的整体遍历方法(forEachRemaining())可以优化遍历并在遍历所有元素之后检查结构干涉,而不是检查每个元素并立即失效。
  • 分离器可以通过该estimateSize() 方法提供剩余元素数量的估计。理想情况下,如特性中所反映的 SIZED,此值正好对应于成功遍历中会遇到的元素数。但是,即使在不确定的情况下,估计值仍可能对在源上执行的操作有用,例如有助于确定是否更优选进一步拆分或遍历其余元素。

尽管分离器在并行算法中具有明显的实用性,但它并不是线程安全的。取而代之的是,使用分离器的并行算法实现应确保分离器一次只能由一个线程使用。通常,这很容易通过串行线程限制来实现,这通常是通过递归分解工作的典型并行算法的自然结果。线程调用 trySplit() 可能会将返回的 Spliterator 移交给另一个线程,后者又可以遍历或进一步拆分该 Spliterator。

如果两个或多个线程在同一个分离器上同时运行,则拆分和遍历的行为是不确定的。如果原始线程将分离器移交给另一个线程进行处理,则最好在使用消耗任何元素之前使用 tryAdvance() 进行该移交,因为某些保证(例如 SIZED 分离器 estimateSize() 的准确性)仅在遍历开始之前有效。

原始子类型的专用分离器提供 int、long、double 值。子类型的默认实现 tryAdvance(java.util.function.Consumer)forEachRemaining(java.util.function.Consumer) 装箱原始值到其相应的包装类的实例。这样的“装箱”可能会破坏使用原始专门化所获得的任何性能优势。为了避免装箱,应该使用相应的基于原语的方法。例如:Spliterator.OfInt.tryAdvance(java.util.function.IntConsumer)Spliterator.OfInt.forEachRemaining(java.util.function.IntConsumer) 应该优先于 Spliterator.OfInt.tryAdvance(java.util.function.Consumer)Spliterator.OfInt.forEachRemaining(java.util.function.Consumer)使用。使用基于装箱的方法遍历原始值 tryAdvance()forEachRemaining() 并不影响遇到转换为装箱值的值的顺序。

分离器像 Iterator 一样用于遍历源元素。该 Spliterator API 旨在通过支持分解和单元素迭代来支持除顺序遍历之外的有效并行遍历。此外,访问元素的协议如果用 Spliterator,每个元素的开销比 Iterator 小,并避免内部快速调用分离方法 hasNext()next()

对于可变源,如果在分隔符绑定到其数据源的时间到遍历结束之间,源在结构上受到干扰(添加,替换或删除的元素),则可能会发生任意和不确定的行为。例如,使用 java.util.stream 框架时,此类干扰将产生任意的不确定结果。

源的结构性干扰可通过以下方式进行管理 (按可取性降低的近似顺序):

  • 源在结构上不能受到干扰。例如,一个 java.util.concurrent.CopyOnWriteArrayList的实例是一个不可变的源
  • 源管理并发修改。例如,一个 java.util.concurrent.ConcurrentHashMap 键集合是并发源。这个源创建的 Spliterator 报告一个 CONCURRENT 特性。
  • 可变源提供了后期绑定和快速故障的 Spliterator。后期绑定会缩小干扰可能影响计算的窗口; 故障快速检测将尽最大努力检测到发生在遍历开始之后的结构性干扰,并抛出 ConcurrentModificationException。例如,JDK 中的 ArrayList 和其他许多非并行 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。则在对原始子类型专门化进行操作时如果发生原始值的装箱会报告诊断警告。

/**    
 * @param <T> the type of elements returned by this Spliterator     
 此分离器返回的元素类型
 *
 * @see Collection
 * @since 1.8
 */
public interface Spliterator<T> 

4.2 方法

4.2.1 tryAdvance(Consumer action)

如果存在剩余元素,则对其执行给定的操作,返回 true;否则返回 false。如果此分隔符是 ORDERED 按遇到顺序对下一个元素执行的操作。该操作引发的异常将中继到调用方。

    /**
     * @param action 动作
     * @return 如果在进入此方法时不存在任何剩余元素则为 false,否则为 true。
     * @throws NullPointerException  如果指定的操作为 nul
     */
    boolean tryAdvance(Consumer<? super T> action);

4.2.2 forEachRemaining(Consumer action)

在当前线程中依次对每个剩余元素执行给定的操作,直到所有元素都已处理或该操作引发异常。如果此分隔符为 ORDERED,则按遇到顺序执行操作。该操作引发的异常将转发到调用方。

默认实现重复调用 tryAdvance 直到返回 false。尽可能覆盖它。

    /**
     * @param action 那个行动
     * @throws NullPointerException 如果指定的操作为 null
     */
    default void forEachRemaining(Consumer<? super T> action) {
        do { } while (tryAdvance(action));
    }

4.2.3 trySplit()

如果该分割器可以分区,则返回一个覆盖元素的分割器,从此方法返回后,该分割器将不覆盖这些元素。

如果此 Spliterator 为 ORDERED,则返回的 Spliterator 必须覆盖元素的严格前缀。

除非此 Spliterator 包含无限数量的元素,否则对的重复调用trySplit() 必须最终返回 null。非空返回时:

estimateSize() 拆分前报告的值,拆分后必须大于等于estimateSize(),并返回分离器。
如果此 Spliterator 为 SUBSIZED,则此分离器的 estimateSize() 在拆分之前必须等于此分离器与拆分后返回的分离器的 estimateSize() 之和。
此方法可能出于任何原因返回 null,包括空、开始遍历后无法拆分、数据结构限制和效率考虑。
一种理想的 trySplit 方法(无需遍历)可以有效地将其元素精确地分成两半,从而实现平衡的并行计算。许多偏离这一理想的做法仍然非常有效。

例如,仅对近似平衡的树进行近似拆分,或者对于其中叶节点可能包含一个或两个元素的树,无法进一步拆分这些节点。但是,平衡大偏差和/或效率过低的 trySplit 机械典型结果会导致较差的并行性能。

    /** 
     * @return 返回一个 Spliterator 覆盖所述元件的一些部分,或者如果这 spliterator 不能分裂则返回 null
     */
    Spliterator<T> trySplit();

4.2.4 estimateSize()

返回 forEachRemaining 遍历将遇到的元素数量的估计值,当无限,未知或计算代价过高则返回 Long.MAX_VALUE

如果这个分离器是 SIZED 并且尚未被部分地遍历或拆分,或者这个拆分器是 SUBSIZED 并且尚未部分遍历,则此估计值必须完整遍历将遇到的元素的精确计数。否则,此估计值可能会任意不准确,但规定在穿过调用 trySplit() 时必须减少。

即使是不精确的估算值,也通常对计算有用且经济。例如,近似平衡的二叉树的子拆分器可能返回一个值,该值估计元素的数量为其父代的一半。如果根分离器不能保持准确的计数,则可以将大小估计为与其最大深度相对应的 2 的幂。

/**
 * @return 估计的大小,当无限,未知或计算代价过高则返回 Long.MAX_VALUE。
 */
long estimateSize();

4.2.5 getExactSizeIfKnown()

如果知道就返回确切的尺寸。 方便的方法,如果 Spliterator 是 SIZED 则返回 estimateSize(),否则返回 -1。

默认实现如果 Spliterator 是 SIZED 则返回 estimateSize(),否则返回 -1。

    /**
     * @return 如果知道返回确切的大小,否则返回-1
     */
    default long getExactSizeIfKnown() {
        return (characteristics() & SIZED) == 0 ? -1L : estimateSize();
    }

4.2.6 characteristics()

返回此拆分器及其元素的一组特征。结果被表示为 DISTINCTSORTEDSIZEDNONNULLIMMUTABLECONCURRENTSUBSIZED 在对 trySplit 的调用之前或之间,对给定拆分器的 characteristics() 的重复调用应始终返回相同的结果。 如果 Spliterator 报告了一组不一致的特征(无论是从单个调用返回的还是跨多个调用返回的),则不能保证使用该 Spliterator 的任何计算。

拆分前给定拆分器的特性可能与拆分后的特性不同。具体示例见特征值 SIZED、SUBSIZED、CONCURRENT

    /**
     * @return a representation of characteristics
     */
    int characteristics();

4.2.7 hasCharacteristics(int characteristics)

如果 Spliterator 的 characteristics() 包含所有给定的特性则返回 true。 默认的实现是如果 Spliterator 的 characteristics() 包含所有给定的特性则返回 true。

    /**
     * @param characteristics 要检查的特征
     * @return 如果所有指定的特征都存在返回 true,否则返回 false
     */
    default boolean hasCharacteristics(int characteristics) {
        return (characteristics() & characteristics) == characteristics;
    }

4.2.8 getComparator()

如果此分离器的来源是 SORTED,是一个 Comparator,则返回 Comparator。如果源是 SORTED 在 Comparable 自然顺序,返回 null。否则,如果不是 SORTED,则抛出 IllegalStateException。 默认实现始终抛出 IllegalStateException

    /**
     * @return 返回 Comparator,如果元素自然排序则返回 null。
     * @throws IllegalStateException 如果分隔符未报告特征 SORTED。
     */
    default Comparator<? super T> getComparator() {
        throw new IllegalStateException();
    }

4.3 常量

4.3.1 ORDERED

表示为元素定义遭遇顺序的特征值。如果是这样,这个 Spliterator 保证方法 trySplit 分割元素的严格前缀,方法 tryAdvance 按一个元素的前缀顺序执行,并且 forEachRemaining 按遭遇顺序执行操作。

如果对应的 Collection.iterator 记录了一个顺序,则 Collection 有一个遭遇顺序。如果是这样,则遭遇顺序与记录的顺序相同。否则,集合没有遭遇顺序。

对于任何 List,相遇顺序保证为升序索引顺序。但是,对于基于散列的集合,例如 HashSet,不能保证顺序。报告 ORDERED 的拆分器的客户机在非交换并行计算中应保持顺序约束。

    public static final int ORDERED    = 0x00000010;

4.3.2 DISTINCT

这个特征值表示,对于每对遇到的元素 x、y,都有 !x.equals(y),即 x 不等于 y。例如,这适用于基于 Set 的拆分器。

    public static final int DISTINCT   = 0x00000001;

4.3.3 SORTED

表示遇到顺序遵循定义的排序顺序的特征值。如果是这样,方法 getComparator()返回关联的比较器,如果所有元素都是 Comparable 并按其自然顺序排序,则返回 null。

报告 SORTED 的拆分器还必须报告 ORDERED。JDK 中实现 NavigableSet 或 SortedSet 的 Collection 类的拆分器报告 SORTED

    public static final int SORTED     = 0x00000004;

4.3.4 SIZED

这个特征值表示,在遍历或拆分之前从 estimateSize() 返回的表示有限大小的值,在没有结构源修改的情况下,表示完全遍历将遇到的元素数的精确计数。

覆盖 Collection 所有元素的集合拆分器都会报告这种特性。子拆分器,例如 HashSet 的那些,覆盖了一个子元素集并近似于它们报告的大小。

    public static final int SIZED      = 0x00000040;

4.3.5 NONNULL

表示源代码保证遇到的元素不会是 null 的特征值。(例如,这适用于大多数并发集合、队列和映射)

    public static final int NONNULL    = 0x00000100;

4.3.6 IMMUTABLE

表示不能在结构上修改元素源的特征值;也就是说,不能添加、替换或删除元素,因此在遍历期间不会发生此类更改。不报告 IMMUTABLECONCURRENT 的拆分器应具有与遍历期间检测到的结构干扰有关的文档化策略(例如抛出 ConcurrentModificationException)。

    public static final int IMMUTABLE  = 0x00000400;

4.3.7 CONCURRENT

表示元素源可以由多个线程安全地并发修改(允许添加、替换和/或删除),而无需外部同步。如果是这样的话,Spliterator 应该有一个关于遍历期间修改影响的文档化策略。

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

大多数并发集合都保持一个一致性策略,以保证 Spliterator 构造时元素的准确性,但可能不会反映后续的添加或删除。

    public static final int CONCURRENT = 0x00001000;

4.3.8 SUBSIZED

表示从trySplit() 生成的所有拆分器都将是 SIZEDSUBSIZED 的特征值。(这意味着所有子拆分器,无论是直接拆分器还是间接拆分器,都将是 SIZED

没有按照 SUBSIZED 的要求报告 SIZED 的拆分器不一致,并且无法保证使用该拆分器进行任何计算。

有些拆分器,例如近似平衡二叉树的顶层拆分器,将报告SIZED,但不报告 SUBSIZED,因为通常知道整个树的大小,但不知道子树的确切大小。

    public static final int SUBSIZED = 0x00004000;

5. Spliterator.OfPrimitive

专门用于原始值的拆分器。

    /**
     * @param <T> 此分离器返回的元素类型。
     该类型必须是对一个原始类型的包装类型,
     如 Integer 为 原始的 int 类型。
     * @param <T_CONS> 原始消费者的类型。
     类型必须是 Consumer 的原始特性之于 T,
     例如 IntConsumer 之于 Integer。
     * @param <T_SPLITR> 基本分离器的类型。
     该类型必须是 Spliterator 的原始特殊化 T,
     例如 Spliterator.OfInt 之于 Integer。
     * @since 1.8
     */
    public interface OfPrimitive<T, T_CONS, T_SPLITR extends Spliterator.OfPrimitive<T, T_CONS, T_SPLITR>>
            extends Spliterator<T> {
        @Override
        T_SPLITR trySplit();
    }

5.1 tryAdvance

如果存在剩余元素,则对其执行给定的操作,返回 true;否则返回 false。

如果此分隔符是 ORDERED 按遇到顺序对下一个元素执行的操作。 该操作引发的异常将中继到调用方。

        /** 
        * @param action 动作
         * @return 如果在进入此方法时不存在任何剩余元素则为 false,否则为 true。
         * @throws NullPointerException 如果指定的操作为 nul
         */
        @SuppressWarnings("overloads")
        boolean tryAdvance(T_CONS action);

5.2 forEachRemaining

在当前线程中依次对每个剩余元素执行给定的操作,直到所有元素都已处理或该操作引发异常。如果此分隔符为 ORDERED,则按遇到顺序执行操作。该操作引发的异常将转发到调用方。

默认实现重复调用 tryAdvance 直到返回 false。尽可能覆盖它。

        /**
         * @param action 那个行动
         * @throws NullPointerException 如果指定的操作为 null
         */
        @SuppressWarnings("overloads")
        default void forEachRemaining(T_CONS action) {
            do { } while (tryAdvance(action));
        }

6. Spliterator.OfInt

一个专门用于 int 值的分离器。

    public interface OfInt extends OfPrimitive<Integer, IntConsumer, OfInt> {

        @Override
        OfInt trySplit();

        @Override
        boolean tryAdvance(IntConsumer action);

        @Override
        default void forEachRemaining(IntConsumer action) {
            do { } while (tryAdvance(action));
        }

        /**
         如果动作是 IntConsumer 的一个实例,
         那么它被强制转换成 IntConsumer 并传递到
         tryAdvance(java.util.function.IntConsumer); 
         否则通过 IntConsumer 装箱的参数 IntConsumer
         并将其传递到 tryAdvance(java.util.function.IntConsumer)实例,
         使动作适应实例 IntConsumer。
         */
        @Override
        default boolean tryAdvance(Consumer<? super Integer> action) {
            if (action instanceof IntConsumer) {
                //action 继承 IntConsumer 直接调用 tryAdvance(IntConsumer)
                return tryAdvance((IntConsumer) action);
            }
            else {
                if (Tripwire.ENABLED)
                    //启用了装箱调试检查则输出日志
                    Tripwire.trip(getClass(),
                                  "{0} calling Spliterator.OfInt.tryAdvance((IntConsumer) action::accept)");
                return tryAdvance((IntConsumer) action::accept);
            }
        }

        /**
         如果动作是 IntConsumer 的一个实例,
         那么它被强制转换成 IntConsumer
         并传递到 forEachRemaining(java.util.function.IntConsumer); 
         否则通过 IntConsumer 装箱的参数 IntConsumer 并将其传递到
         forEachRemaining(java.util.function.IntConsumer)实例,
         使动作适应实例 IntConsumer。
         */
        @Override
        default void forEachRemaining(Consumer<? super Integer> action) {
            if (action instanceof IntConsumer) {//action 继承 IntConsumer 直接调用
                forEachRemaining((IntConsumer) action);
            }
            else {
                if (Tripwire.ENABLED)//启用了装箱调试检查则输出日志
                    Tripwire.trip(getClass(),
                                  "{0} calling Spliterator.OfInt.forEachRemaining((IntConsumer) action::accept)");
                forEachRemaining((IntConsumer) action::accept);
            }
        }
    }

7. Spliterator.OfLong

一个专门用于 long 值的分离器。

    public interface OfLong extends OfPrimitive<Long, LongConsumer, OfLong> {

        @Override
        OfLong trySplit();

        @Override
        boolean tryAdvance(LongConsumer action);

        @Override
        default void forEachRemaining(LongConsumer action) {
            do { } while (tryAdvance(action));
        }

        /**
         如果动作是 LongConsumer 的一个实例,
         那么它被强制转换成 LongConsumer 并传递到
         tryAdvance(java.util.function.LongConsumer); 
         否则通过 LongConsumer 装箱的参数 LongConsumer
         并将其传递到 tryAdvance(java.util.function.LongConsumer)实例,
         使动作适应实例 LongConsumer。
         */
        @Override
        default boolean tryAdvance(Consumer<? super Long> action) {
            if (action instanceof LongConsumer) {
                return tryAdvance((LongConsumer) action);
            }
            else {
                if (Tripwire.ENABLED)
                    Tripwire.trip(getClass(),
                                  "{0} calling Spliterator.OfLong.tryAdvance((LongConsumer) action::accept)");
                return tryAdvance((LongConsumer) action::accept);
            }
        }

        /**
         如果动作是 LongConsumer 的一个实例,
         那么它被强制转换成 LongConsumer 并传递到
         forEachRemaining(java.util.function.LongConsumer); 
         否则通过 LongConsumer 装箱的参数 LongConsumer
         并将其传递到 forEachRemaining(java.util.function.LongConsumer)实例,
         使动作适应实例 LongConsumer。
         */
        @Override
        default void forEachRemaining(Consumer<? super Long> action) {
            if (action instanceof LongConsumer) {
                forEachRemaining((LongConsumer) action);
            }
            else {
                if (Tripwire.ENABLED)
                    Tripwire.trip(getClass(),
                                  "{0} calling Spliterator.OfLong.forEachRemaining((LongConsumer) action::accept)");
                forEachRemaining((LongConsumer) action::accept);
            }
        }
    }

8. Spliterator.OfDouble

一个专门用于 double 值的分离器。

    public interface OfDouble extends OfPrimitive<Double, DoubleConsumer, OfDouble> {

        @Override
        OfDouble trySplit();

        @Override
        boolean tryAdvance(DoubleConsumer action);

        @Override
        default void forEachRemaining(DoubleConsumer action) {
            do { } while (tryAdvance(action));
        }

        /**
         如果动作是 DoubleConsumer 的一个实例,
         那么它被强制转换成 DoubleConsumer 并传递到
         tryAdvance(java.util.function.DoubleConsumer);
          否则通过 DoubleConsumer 装箱的参数 DoubleConsumer
          并将其传递到 tryAdvance(java.util.function.DoubleConsumer)实例,
          使动作适应实例 DoubleConsumer。
         */
        @Override
        default boolean tryAdvance(Consumer<? super Double> action) {
            if (action instanceof DoubleConsumer) {
                return tryAdvance((DoubleConsumer) action);
            }
            else {
                if (Tripwire.ENABLED)
                    Tripwire.trip(getClass(),
                                  "{0} calling Spliterator.OfDouble.tryAdvance((DoubleConsumer) action::accept)");
                return tryAdvance((DoubleConsumer) action::accept);
            }
        }

        /**
         如果动作是 DoubleConsumer 的一个实例,
         那么它被强制转换成 DoubleConsumer 并传递到
         forEachRemaining(java.util.function.DoubleConsumer);
          否则通过 DoubleConsumer 装箱的参数 DoubleConsumer 并将其传递到
          forEachRemaining(java.util.function.DoubleConsumer)实例,
          使动作适应实例 DoubleConsumer。
         */
        @Override
        default void forEachRemaining(Consumer<? super Double> action) {
            if (action instanceof DoubleConsumer) {
                forEachRemaining((DoubleConsumer) action);
            }
            else {
                if (Tripwire.ENABLED)
                    Tripwire.trip(getClass(),
                                  "{0} calling Spliterator.OfDouble.forEachRemaining((DoubleConsumer) action::accept)");
                forEachRemaining((DoubleConsumer) action::accept);
            }
        }
    }

9. Tripwire

用于在 java.util 类中检测装箱的意外使用的实用程序类。它会检查开关基于系统属性 org.openjdk.java.util.stream.tripwire 根据 Boolean.getBoolean(String) 定义为 true、这通常应关闭以供生产使用。

相关文章

网友评论

      本文标题:从底层重学 Java 之 Stream 分离器及 Sink

      本文链接:https://www.haomeiwen.com/subject/cjytgktx.html