Iterator源码分析

源码如下

public interface Iterator {
    boolean hasNext();

    E next();

    default void remove() {
        throw new UnsupportedOperationException("remove");
    }

    default void forEachRemaining(Consumer action) {
        Objects.requireNonNull(action);

        while(this.hasNext()) {
            action.accept(this.next());
        }

    }
}

hasNext()方法和next()方法会在Iterator接口的实现类中实现

当我们调用ArrayLIst.iterator()时,我们调用的是该类中的:

public Iterator iterator() {
        return new ArrayList.Itr();
    }

进一步发现Itr是ArrayList中的一个内部类

private class Itr implements Iterator {
        int cursor;//光标,游标
        int lastRet = -1;
        int expectedModCount;
        ......
}

当我们这样调用时:

Iterator it = ArrayList.iterator();

实际上是将ArrayList的内部类对象赋给了it

然后我们看Itr的构造方法

 Itr() {
            this.expectedModCount = ArrayList.this.modCount;
        }

构造方法中为expectedModCount赋初值为modCount,这个成员变量是ArrayList继承于AbstractList的

然后我们调用it的方法其实就是在调用Itr的方法:

 public boolean hasNext() {
            return this.cursor != ArrayList.this.size;
        }
 public E next() {
            this.checkForComodification();
            int i = this.cursor;
            if (i >= ArrayList.this.size) {
                throw new NoSuchElementException();
            } else {
                Object[] elementData = ArrayList.this.elementData;
                if (i >= elementData.length) {
                    throw new ConcurrentModificationException();
                } else {
                    this.cursor = i + 1;
                    return elementData[this.lastRet = i];
                }
            }
        }

hasNext()方法

hasNext的判断条件为cursor!=size,就是当前迭代的位置不是数组的最大容量值就返回true

next()方法

首先调用checkForComodification()方法判断是否并发修改,然后使用cursor光标返回elementData中序号为cursor的对象,同时cursor+1

cursor的初始值是0,lastRet的初始值是-1,cursor始终等于lastRet+1

remove()方法

 public void remove() {
            if (this.lastRet < 0) {
                throw new IllegalStateException();
            } else {
                this.checkForComodification();

                try {
                    ArrayList.this.remove(this.lastRet);
                    this.cursor = this.lastRet;
                    this.lastRet = -1;
                    this.expectedModCount = ArrayList.this.modCount;
                } catch (IndexOutOfBoundsException var2) {
                    throw new ConcurrentModificationException();
                }
            }
        }

remove()方法调用ArrayList自身的remove方法,传入的参数为lastRet的值,删除元素后,将lastRet的值赋给cursor,因为删除一个元素后光标需要退一,同时将lastRet重新置为-1,将expectedModCount重新置为modCount,因为方法中调用了ArrayList的remove方法,所以modCount+1,所以要更新expectedModCount的值

给出一段测试代码

 public static void main(String[] args){
          List list = new ArrayList();
            //CopyOnWriteArrayList list = new CopyOnWriteArrayList();
            list.add("a");
            Iterator iterator = list.iterator();
            while(iterator.hasNext()){
                String str = (String) iterator.next();
                list.remove(str);
            }
    }
//Exception in thread "main" java.util.ConcurrentModificationException
    //at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1042)
    //at java.base/java.util.ArrayList$Itr.next(ArrayList.java:996)

从报错信息来看,抛出的是并发修改类型(ConcurrentModificationException),发生在调用Itr类的next()方法时

为什么会抛出这样的异常呢

我们看到这个集合里面只有一个元素,当iterator调用第一个next()方法时,首先进行checkForComodification()判断,这个时候expectedModCount肯定是与modCount相等的,因为构造函数里面刚刚完成赋值;然后调用了ArrayList的remove方法,这时modCount+1,而expectedModCount仍为原值,
再次调用hasNext方法时,由于cursor为1,ArrayList.size为0,所以返回值仍为true,再次进入循环,而此时第二次调用next()方法时,在checkForComodification()时就会抛出异常

而当我们换成调用iterator的remove()方法时,程序并不会报错,因为此时根本不会进入第二次循环,而且this.expectedModCount = ArrayList.this.modCount;使两者保持同步,也为以后的方法调用扫除了隐患

类似这样的并发修改异常还有:

 public static void main(String[] args){
          List list = new ArrayList();
            //CopyOnWriteArrayList list = new CopyOnWriteArrayList();
            list.add("a");
            list.add("b");
            list.add("c");
            list.add("d");
            list.add("e");
            Iterator iterator = list.iterator();
            while(iterator.hasNext()){
                String str = (String) iterator.next();
                if(str.equals("d")){
                    list.remove(str);
                }else{
                    System.out.println(str);
                }
            }
    }
//a
//b
//c

这样的程序不会报错,因为当remove("d")时,cursor=4,而删除d后,size=4,从而跳出循环,e也不会输出了

同样的,将list.remove(str);换成iterator.remove();后,程序正常执行,输出结果为 a b c e,这样才是正确的结果

参考
https://www.cnblogs.com/zuochengsi-9/p/7050351.html
https://www.cnblogs.com/dolphin0520/p/3933551.html
https://blog.csdn.net/weixin_40254498/article/details/81386920

你可能感兴趣的:(Iterator源码分析)