测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
public static void main(String[] args) {

List<String> list = new Vector<>();
list.add("1");
list.add("2");
for(String it:list){
// 如果it==2,删除it
if("2".equals(it)){
list.remove(it);
}
}
}

如果运行上述代码,会抛出ConcurrentModificationException异常。

为什么会报错呢?

javap -c命令查看编译后代码,了解下上述代码运行过程:

foreach

上图代码A处,可以看到foreach循环是使用的是Iterator迭代器,代码B处使用Iterator迭代器中的next方法获取集合中的值,代码C处使用list.remove()删除集合中的值。

Itr迭代器类的next方法:

1
2
3
4
5
6
7
8
9
10
11
public E next() {
synchronized (Vector.this) {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
int i = cursor;
if (i >= elementCount)
throw new NoSuchElementException();
cursor = i + 1;
return elementData(lastRet = i);
}
}

list.remove方法代码:

1
2
3
4
5
6
7
8
9
10
public synchronized boolean removeElement(Object obj) {
// 修改了modCount
modCount++;
int i = indexOf(obj);
if (i >= 0) {
removeElementAt(i);
return true;
}
return false;
}

首先next方法中,首先判断modCount的值,如果modCount的值和expectedModCount值不一致,就会抛出ConcurrentModificationException异常。而恰恰remove方法中就修改了modCount的值,而未修改expectedModCount的值,从而导致我们的测试代码抛出ConcurrentModificationException异常。

抛错的源头找到了,可是为什么会出现这种问题呢?

我们看下导致抛出源头的两个参数modCount和expectedModCount。modCount是类Vector的成员变量,主要记录Vector的修改次数;expectedModCount是内部迭代器类Itr的成员变量,初始化时等于modCount。我们使用foreach去remove集合内的值时,调用的是Vector类的remove方法,不是迭代器Itr的remove方法,所以只会修改modCount的值,而不更新expectedModCount的值。所以在测试代码中报错是因为在第一次删除后,modCount值+1,而expectedModCount的值未变化,所以在迭代器再次调用next方法获取集合内的值时就会抛出ConcurrentModificationException异常。