以下的内容摘抄自:http://www.cnblogs.com/dolphin0520/p/3933551.html
ConcurrentModificationException异常出现的原因
使用集合的时候,通常希望遍历到需要修改的集合后,直接利用集合对象的remove方法直接删除或者修改对应的值。但是!这种方法是不可行的,调试过的小伙伴儿应该知道,这样做会抛出一个ConcurrentModificationException异常。下面就是出现该异常的情况:
public class ArrayListTest {
public static void main(String[] args) {
List l = new ArrayList();
l.add(new String("aa"));
l.add(new String("bb"));
l.add(new String("cc"));
l.add(new String("dd"));
System.out.println("修改前的值:"+l);
Iterator iterator = l.iterator();
while(iterator.hasNext()){
//将iterator遍历到的值赋给obj
Object obj = iterator.next();
if(obj.equals("aa")) {
//找到想要修改的值后,直接修改该值
l.add(new String("modify"));
}
}
System.out.println("修改后的值:"+l);
}
}
程序运行结果:
修改前的值:[aa, bb, cc, dd]
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
at java.util.AbstractList$Itr.next(AbstractList.java:343)
at set.ArrayListTest.main(ArrayListTest.java:21)
控制台输出的结果显示,该异常出现在AbstractList类的checkForComodification()方法中。该方法的源码如下:
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
先不解释源码,先根据刚刚出错的程序代码一步一步看ArrayList源码的实现。
那我们就先看看ArrayList的父类AbstractList中的iterator()
方法。下面是AbstractList类中iterator()
方法的源码:
public Iterator<E> iterator() {
return new Itr();
}
这个方法只返回了一个指向Irt类型的对象引用,接着查找AbstractList类中Itr类的具体实现
private class Itr implements Iterator<E> {
/**
* Index of element to be returned by subsequent call to next.
*/
int cursor = 0; //表示下一个要访问的元素的索引
/**
* Index of element returned by most recent call to next or
* previous. Reset to -1 if this element is deleted by a call
* to remove.
*/
int lastRet = -1; //表示上一个访问的元素的索引
/**
* The modCount value that the iterator believes that the backing
* List should have. If this expectation is violated, the iterator
* has detected concurrent modification.
*/
int expectedModCount = modCount;表示对ArrayList修改次数的期望值,它的初始值为modCount。
public boolean hasNext() {
return cursor != size();
}
public E next() {
checkForComodification();
try {
int i = cursor;
E next = get(i);
lastRet = i;
cursor = i + 1;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public void remove() {
if (lastRet < 0)
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();
}
}
该内部类有三个成员变量:
- cursor:表示下一个要访问的元素的索引,从
next()
方法的具体实现可以看出来 - lastRet:表示上一个访问的元素的索引
exceptionModCount:表示对ArrayList修改次数的期望值,它的初始值为modCount
modCount是AbstractList类的一个成员变量:protected transient int modCount = 0;
该值表示对List的修改次数,在ArrayList中的add()
方法addAll
方法和remove()
等方法中就可以知道,每次调用上述方法,就会对modCount的值加1。
继续看上述出现异常的例子:
- 当调用了
l.next()
后,返回一个迭代器对象iterator,通过Iterator的hashNext()方法判断是否还有元素未被访问,我们看一下hasNext()方法,hashNext()方法的实现很简单:
public boolean hasNext() {
return cursor != size();
}
- 这个方法实现的很简单,就是一下个访问的元素下标不等于ArrayList中存的数据量,就表示有元素需要访问。
- 然后通过iterator的
next()
方法获取到下标为0的元素,下面展示next()
方法的具体实现:
public E next() {
checkForComodification();
try {
int i = cursor;
E next = get(i);
lastRet = i;
cursor = i + 1;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
- 首先会调用
checkForComodification()
方法; - 用变量i获取下一个元素的下标;
- 然后根据i获取元素;
- 将i的值赋给lastRet,然后让cursor+1
- 将获得的元素返回
调用第一次之后,cursor的值为1,lastRet的值为0,但是这个时候modCount=0,expectedModCount也为0
接着继续进行循环,程序中判断当前元素的值是否等于aa
,如果等于2,就调用l.remove()
方法来删除该元素。下面是ArrayList中remove()
方法的源码:
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // Let gc do its work
return oldValue;
}
通过remove()
方法删除元素,经过如下几步骤:
- 首先检查删除元素的下标范围
- 对modCount++(因为对集合修改了一次)
- 通过调用
elementData(index)
方法获取该下标元素,该方法的源码:
E elementData(int index) {
return (E) elementData[index];
}
- 然后接下来就是删除元素的操作,最后将size进行减1操作,并将引用置为null以方便垃圾收集器进行回收工作。
- 下面需要注意各个变量的值:
- 对于iterator,其expectedModCount为0,cursor的值为1,lastRet的值为0。
- 对于list,其modCount为1,size为0。
接着看程序代码,执行完删除操作后,继续while循环,调用hasNext方法()判断,由于此时cursor为1,而size为0,那么返回true,所以继续执行while循环,然后继续调用iterator的next()方法:
注意,此时要注意next()方法中的第一句:checkForComodification()。
在checkForComodification方法中进行的操作是:
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
如果modCount不等于expectedModCount,则抛出ConcurrentModificationException异常。
很显然,此时modCount为1,而expectedModCount为0,因此程序就抛出了ConcurrentModificationException异常。
【注意】调用l.remove()方法导致modCount和expectedModCount的值不一致。