拆行解码 Java 集合源码之迭代器

737 阅读6分钟

Java 集合源码解析系列:

迭代器

还有一个接口 Iterator,每个集合类内部通过实现该接口,实现集合元素的遍历。将遍历序列的操作与序列底层的结构分离。

该对象必须依赖于具体容器,因为每一个容器的数据结构不同;

所以迭代器对象是在容器中进行实现的(内部类)。

迭代的时候只能进行删除(可选操作)。

for(Iterator it = Collection.iterator(); it.hasNext();){
		E e=it.next();
  	it.remove();
}

ps:1.2 之前是通过 Enumeration 接口实现遍历。

不是快速失败的!!

支持的集合较少,Hashtable 的键、值集合,Vector。

Iterator 允许遍历期间删除元素,方法命名更加友好。

foreach 与 迭代器

(1)foreach主要是针对数组或任何实现 Iterable 接口的类。

public interface Iterable<T> {

    Iterator<T> iterator();

    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }
    /**
    * 返回一个可能并行的可拆分迭代器。
    * 是基于原数据源(集合、数组、IO 流)的可拆分迭代器
    * 虽然可以在并行流程中使用,但是并不是线程安全,尤其是拆分的过程。
    * 建议在单线程的串行线程中使用,比如基于分治的任务分解过程。
    */
    default Spliterator<T> spliterator() {
        return Spliterators.spliteratorUnknownSize(iterator(), 0);
    }
}

(2)集合进行 foreach 循环时,会默认调用重写的 iterator 方法,得到实现 iterable 的迭代器对象 Iterator,从而进行遍历。(实际是一种语法糖)

for (Integer e : list) {
  // do some thing
}
=> 
object = list.iterator();
while (object.hasNext()) {
  Integer e = object.next());
}

(3)集合可以写一个方法,返回一个实现了 Iterable 接口的 Iterator 类,进行不同方式的迭代。

注意:数组直接使用 foreach 进行遍历即可。不能够将数组转化为 Iterable 接口对象进行遍历,只能人为使用 Arrays.asList() 转化为 ArrayList 对象,然后默认调用 Iterator 方法进行遍历。

快速失败(fail fast)

集合在使用迭代器在遍历过程中,不能使用集合的 remove() 方法进行增删(修改可以),否则抛出ConcurrentModificationException。

只能使用迭代器的 remove 方法,方法内部对集合进行同步删除操作。

原理:将计数器与迭代器关联起来。在遍历每一个元素的时候都会检查一遍,返回迭代器时记录的expectedModCount 与现在容器的 ModCount 是否相等。(modCount 的含义是记录该容器被修改的次数,包括但不限于更改容器大小或其他可能干扰到迭代器的操作。

如果使用容器的api进行操作,将无法同时更新两个变量的值,会导致失败。而迭代器的删除会同时维护索引表的内容,保证一致性。

快速失败并不是严谨保证不同步下的并发安全性,应该被用于检查bug。

**** 可拆分迭代器

public interface Spliterator<T> {
    // 一个数据源的Spliterator允许有多个特征,定义的特征通过 或运算 得到一个数据源的特征 characteristics()。

    /**
     * 标识数据源的元素是有序的
     */
    public static final int ORDERED    = 0x00000010;

    /**
     * 标识数据源的元素是不重复的。比如基于 Set的
     */
    public static final int DISTINCT   = 0x00000001;

    /**
     * 数据源的元素是按照一定规律排序的。
     * 基于该特征,要么getComparator() 有值,优先;
     * 要么, 元素是实现 Comparable 接口。
     * 
     * 同时具备 ORDERED 的特征。
     * 比如基于 SortedSet。
     */
    public static final int SORTED     = 0x00000004;

    /**
     * 大小固定。
     * 在遍历或拆分之前,通过 estimateSize() 得到的是数据源未修改情况下的精确计数。
     * 大部分 Collection 的 Spliterator 会具备该特性。
     */
    public static final int SIZED      = 0x00000040;

    /**
     * 数据源不为空。大部分的并发集合、队列、map。
     */
    public static final int NONNULL    = 0x00000100;

    /**
     * 不可变。标识遍历期间,不可添加、删除、替换。
     */
    public static final int IMMUTABLE  = 0x00000400;

    /**
     * 并发安全。在多线程遍历的情况下,线程安全地添加、替换、删除。
     * 一般不会同时存在该特征和 SIZED 或 IMMUTABLE。
     */
    public static final int CONCURRENT = 0x00001000;

    /**
     * 通过 trySplit() 拆分出的子迭代器,同时具有 SUBSIZED 和 SIZED。
     * 简单来说,所有的子迭代器,无论是直接拆分的,还是间接的,都是 SIZED。
     */
    public static final int SUBSIZED = 0x00004000;

    /**
     * 如果存在元素,则对其执行 action;
     */
    boolean tryAdvance(Consumer<? super T> action);

    /**
     * 对余下的元素,都执行action;直到遍历完,或遇到异常。
     */
    default void forEachRemaining(Consumer<? super T> action) {
        do { } while (tryAdvance(action));
    }

    /**
     * 拆分当前 Spliterator。得到的 Spliterator 遍历的元素,将不再被当前 Spliterator 所包含。
     * 
     * 如果当前是 ORDER 的,那么得到的 Spliterator 将会遍历一组相同特征(前缀)的元素。
     *
     * 除非当前是无限元素,否则最终将得到 NULL。
     * 
     * 拆分前 estimateSize() 必定大于等于拆分后的。
     * 如果是 SUBSIZED,那么拆分前的等于拆分后的总和。
     *
     * 此方法可能由于任何原因返回null,包括空闲,在遍历开始后无法拆分,数据结构约束和效率考虑。
     * 
     * 注意: 理想的trySplit方法有效地(无遍历)将其元素精确地分成两半,允许平衡并行计算。
     * 许多偏离这种理想仍然非常有效;例如,仅近似地拆分一个近似平衡的树,或者叶子节点可能包含一个或两个元素的树,无法进一步拆分这些节点。 
     * 然而,平衡的大偏差和/或过低效率的trySplit机制通常会导致较差的并行性能。
     */
    Spliterator<T> trySplit();

    /**
     * 得到一个用于表示 forEachRemaining 还可以遍历多少的预估值。特别特别注意,不是全部,而是剩余可遍历。
     * 或者如果是无限,未知,或太大难以计算,则返回 Long.MAX_VALUE
     *
     * 如果是 SIZED,且未拆分或未部分遍历;或SUBSIZED且未部分遍历,则返回值应该是接下来遍历的准确计数。
     * 否则,此估算值可能是任意不正确的,但必须按照{@link #trySplit}调用中指定的那样减小。
     */
    long estimateSize();

    
    default long getExactSizeIfKnown() {
        return (characteristics() & SIZED) == 0 ? -1L : estimateSize();
    }

    /**
     * 上述定义的枚举数值的或运算结果。
     * 应该是不变的,重复调用也有相同的结果。
     * 
     * 拆分前后可能不同。
     */
    int characteristics();

    default boolean hasCharacteristics(int characteristics) {
        return (characteristics() & characteristics) == characteristics;
    }

    /**
     * 如果此Spliterator的源由Comparator比较器为SORTED,则返回该Comparator。
     * 如果源按Comparable自然顺序为SORTED,则返回null。否则,如果源不是SORTED,则抛出IllegalStateException。
     */
    default Comparator<? super T> getComparator() {
        throw new IllegalStateException();
    }

    /**
     * A Spliterator specialized for primitive values.
     */
    public interface OfPrimitive<T, T_CONS, T_SPLITR extends Spliterator.OfPrimitive<T, T_CONS, T_SPLITR>>
            extends Spliterator<T> {}

    /**
     * A Spliterator specialized for {@code int} values.
     * @since 1.8
     */
    public interface OfInt extends OfPrimitive<Integer, IntConsumer, OfInt> {}

    /**
     * A Spliterator specialized for {@code long} values.
     * @since 1.8
     */
    public interface OfLong extends OfPrimitive<Long, LongConsumer, OfLong> {}

    /**
     * A Spliterator specialized for {@code double} values.
     * @since 1.8
     */
    public interface OfDouble extends OfPrimitive<Double, DoubleConsumer, OfDouble> {}
}