日常代码编写的时候不难发现:例如在使用迭代器遍历集合的 同时若进行删改操作,会抛出JavaConcurrent Modification Exce ption 异常,而不使用迭代器则不会出现这个异常,例如下图:
那么,看起来似乎没有逻辑问题的代码为什么会报错呢?
控制台显示异常出现在chackForComodification()方法中
为探求其本质,博主去查看了底层源代码:
1.进入ArrayList的 iterator方法,在其父类AbstractList中的iterator 返回了一个Itr对象,点击查看具体实现看到了如下代码:
private class Itr implements Iterator<E> {
int cursor = 0;
int lastRet = -1;
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size();
}
public E next() {
checkForComodification();
try {
E next = get(cursor);
lastRet = cursor++;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public void remove() {
if (lastRet == -1)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
2.其中成员变量:cursor 表示下一个访问元素的索引
lastRet表示上一个访问元素的索引
expectedModCount表示对List修改次数期望
ModCount表示对List修改次数
而如下代码,抛出了这个异常:
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
3.即当ModCount与expectedModCount的值不等的时候,会出现这个异常,而这两个值,正跟add,remove操作息息相关;
以add()方法为例,查看其源码如下:
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
通过对这里的ensureCapacityInternal寻根求源,找到以下源码:
这个方法底层调用了grow方法来实现,而每次调用remove()或者add()都会使得modCount自增一
4.回到程序中:当使用迭代器iterator引用调用hasNext方法时,其源代码实现很简单:hasNext通过比较下一个访问元素下标是否等于size,若等于则判定其为最后一个元素,copy如下:
public boolean hasNext() {
return cursor != size;
}
5.随后进入while中的if判断,iterator引用调用next方法时,进入iterator查询到next方法源码如下:
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
在next方法中,调用了这个checkForComodification();而这里正是抛出异常的位置,然而为什么会报错?程序运行到这里,ModCount与expectedModCount的值好像没有变化啊?接着往下分析,当while循环中的if判断到程序需要修改的元素的地方时:执行了一个add方法:al.add(“C”);
ctrl加回车点进去算是大彻大悟了,原来在add方法中调用了
ensureCapacityInternal方法,而正是这个ensureCapacityInternal方法的底层调用使得modCount的值增加了1!
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
6.而当下一次next()执行时,在前面调用 checkForComodification()方法时,发现ModCount与expectedModCount的值不等,于是便出现了这个异常
7.在remove方法在也是同样的原因,都是底层方法让ModCount值自增而导致ModCount与expectedModCount的值不等,而抛出异常,则也解释了为什么普通fori并没有这个问题,因为普通fori并不涉及到迭代器,自然不会有这个异常