ConcurrentModificationException–java.lang.RuntimeException,一个运行时错误。
此类在源码中的注解为:
/**
* An {@code ConcurrentModificationException} is thrown when a Collection is
* modified and an existing iterator on the Collection is used to modify the
* Collection as well.
*/
意思大致为:在一个集合被修改以及一个现有的集合的迭代器被用于修改此集合时会抛出一个此异常。
下面从List开始分析,List是Java中的一个Interface(接口),List中定义了add()、get()、contain()、remove()、iterator()等方法(iterator()方法返回的为一个Iterator对象)。List接口被AbstractList实现,此类为一抽象类(从1.2版本开始出现,ArrayList并不是直接实现List接口),ArrayList继承AbstractList具体重写了add()等方法,同时ArrayList中有一内部类ArrayListIterator,此类具有hasNext()与next()方法。
使用Iterator与foreach循环list原理都是使用的ArrayListIterator,执行ArrayListIterator的hasNext()与next()方法。
@Override public Iterator<E> iterator() {
return new ArrayListIterator();
}
ArrayListIterator中的hasNext()与next()方法为:
public boolean hasNext() {
return remaining != 0;
}
@SuppressWarnings("unchecked") public E next() {
ArrayList<E> ourList = ArrayList.this;
int rem = remaining;
if (ourList.modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
if (rem == 0) {
throw new NoSuchElementException();
}
remaining = rem - 1;
return (E) ourList.array[removalIndex = ourList.size - rem];
}
hasNext()方法中的remaining值初始为list大小,随着每循环一次,该值会在next()方法中减1,直至为0,hasNext()方法返回false,也就不再执行next()方法。
next()方法中会判断ourList.modCount的值与expectedModCount值是否相等,其实异常的根源就出自于此,expectedModCount是ArrayListIterator的一个属性,初始化被赋予modCount的值。
而modCount为AbstractList属性,值为list的大小,此变量在remove()方法中执行的++操作,因此如果在foreach或者使用Iterator循环中对除最后一个元素之外的其他元素执行remove()操作,modCount的值会被改变,从而在next()方法中的
if (ourList.modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
判断会抛出ConcurrentModificationException。
此异常也有特例,上边说了对除最后一个元素之外的元素执行remove操作才会报异常,那么如果是对最后一个元素执行删除操作呢?答案是不会抛出异常的,虽然modCount变量值在remove()方法中改变了,但在循环到最后一个元素时remaining的值已经变为了0,所以hasNext()方法返回true,不在执行next()方法,因此不在抛出异常。
关于此异常还是有解决办法的,Java中提供有CopyOnWriteArrayList,可以使用此类代替ArrayList,关于CopyOnWriteArrayList会在之后进行分析。