JDK8源码之Iterator顺序遍历迭代器

来源:互联网 发布:什么是java设计模式 编辑:程序博客网 时间:2024/05/30 02:22

Iterator是什么?

public interface Iterator<E> {...}

iterator被称之为顺序遍历迭代器,jdk中默认对集合框架中数据结构做了实现。iterator在实际应用中有一个比较好的点就是,可以一边遍历一遍删除元素(不过需要自实现,好在jdk都帮你做了,哈哈),后面拿ArrayList.iterator()来说明怎么做到这点的。

Iterator结构

 //是否能遍历到下一个元素。是返回true,否则返回false boolean hasNext(); //指向下一个元素 E next(); //删除当前元素,需要实现者重写,默认会抛错 default void remove() {       throw new UnsupportedOperationException("remove");   }  //顺序遍历元素,对每个元素执行指定事件,一直到遍历完所有元素 default void forEachRemaining(Consumer<? super E> action) {    Objects.requireNonNull(action);    while (hasNext())        action.accept(next());    }

ArrayList.iterator()解析

 public Iterator<E> iterator() {        //返回一个顺序遍历迭代器        return new Itr();    } private class Itr implements Iterator<E> {     //指向下次将遍历到的位置     int cursor;        //当前遍历到的位置,当为-1时,表示未指向元素        int lastRet = -1;     //结构变更次数     int expectedModCount = modCount;    //是否返拥有下个元素     public boolean hasNext() {         return cursor != size;     }     //遍历到下个元素     @SuppressWarnings("unchecked")     public E next() {         //ArrayList是否发生结构变更操作         checkForComodification();         //得到当前遍历的位置         int i = cursor;         //是否超出size大小         if (i >= size)             throw new NoSuchElementException();         Object[] elementData = ArrayList.this.elementData;         //并发修改异常,elementData的结构发生变更         //(疑惑:这一段代码的意义,可以和forEachRemaining对比一下)         if (i >= elementData.length)             throw new ConcurrentModificationException();          //指向下次遍历到的位置         cursor = i + 1;         //返回当前位置的值         return (E) elementData[lastRet = i];     }     public void remove() {         //如果当前遍历器未指向任何元素时,则报错         //未执行next()时和执行remove方法时 lastRet=-1         if (lastRet < 0)             throw new IllegalStateException();         checkForComodification();         try {             //调用arrayList.remove方法来删除元素             ArrayList.this.remove(lastRet);             //指针指向当前删除位置             cursor = lastRet;             //lastRet置为-1,表示当前遍历器未指向任何元素             lastRet = -1;             //这里是iteraotr可以遍历到元素后可以删除元素的关键             expectedModCount = modCount;         } catch (IndexOutOfBoundsException ex) {             throw new ConcurrentModificationException();         }     }     @Override     @SuppressWarnings("unchecked")     public void forEachRemaining(Consumer<? super E> consumer) {         //consumer不能为空校验         Objects.requireNonNull(consumer);         final int size = ArrayList.this.size;         int i = cursor;         //遍历器还有元素未遍历完         if (i >= size) {             return;         }         final Object[] elementData = ArrayList.this.elementData;         //并发修改异常,elementData的结构发生变更(这一段代码的意义,可以思考下)         if (i >= elementData.length) {             throw new ConcurrentModificationException();         }         //遍历元素执行指定动作         while (i != size && modCount == expectedModCount) {             consumer.accept((E) elementData[i++]);         }         // update once at end of iteration to reduce heap write traffic         cursor = i;         lastRet = i - 1;         checkForComodification();     }     final void checkForComodification() {         if (modCount != expectedModCount)             throw new ConcurrentModificationException();     } }

看到这一段代码的时候,我对forEachRemaining方法里面检查ConcurrentModificationException并发操作异常的情况有疑惑,在校验元素的size后,再将执行 Object[] elementData = ArrayList.this.elementData;再对elementData进行遍历操作同时并会对expectedModCount进行判断,因此遍历之前检查(i >= elementData.length) 感觉并没有什么用。

另外,ArrayList自身就不是线程安全的,modCount也只是用来保证快速失败,因此不要在多线程的环境下使用iterator进行并行遍历操作。如果需要并行遍历可以使用Spliterator进行并行遍历。

ArrayList.iterator本质上还是操作原list对象,只是通过一个单向指向list的索引而对原对象进行引用,因此iterator在工作的时候,我们如果对远list对象进行了结构上的变更,此时会通过expectedModCount比较modCount是否相等来实现fail-fast。而ArrayList.iterator.remove方法没有问题,是因为操作过后会同步更新expectedModCount的值。

0 0