示例代码:
public class ArrayListTest {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("hello");
list.add("hello1");
list.add("hello2");
list.add("hello3");
list.add("hello4");
list.add("hello5");
for(String s : list){
list.remove("hello2");
}
}
}
报错结果:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at com.collection.list.ArrayListTest.main(ArrayListTest.java:18)
再看这个问题之前大家先来了解一下foreach的本质是什么,
参考此博文:https://blog.csdn.net/wangjun5159/article/details/61415263,foreach只是语法糖,本质还是
for(Iterator i$ = a.iterator(); i$.hasNext(); System.out.print(temp));
然后追踪栈轨迹看看源码:
@SuppressWarnings("unchecked")
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];
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
上面两个方法都是private class Itr implements Iterator 这个迭代器实现类的方法。有上面两个方法可以明显的看出 if (modCount != expectedModCount)这个判断上,是这两个值不相等导致抛出了异常,那么这两个值又是什么呢?
//The number of times this list has been <i>structurally modified</i>.
protected transient int modCount = 0;
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
}
modCount变量是ArrayList继承的抽象类AbstractList中一个变量,它表示的是对list的修改次数,而迭代器中的expectedModCount 在初始的时候,将modCount赋给了expectedModCount ,通过上面foreach我们看得到迭代器是在已进入for循环的时候初始的这时候modCount是未进行修改的,但是接下来remove有干了什么呢?
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
如上代码,看到remove的元素如果存在的话会进入下面这个方法:
private void fastRemove(int index) {
modCount++;//在这里操作了modCount这个变量
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData,
index,numMoved);
elementData[--size] = null; // clear to let GC do its work
}
看到这里我们明白了什么时候改变了modCount导致和expectedModCount 不一致抛出了错误。