源码如下
public interface Iterator {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
default void forEachRemaining(Consumer super E> 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