您的位置:首页 > 编程语言 > Java开发

java.util.ConcurrentModificationException详解

2015-07-29 10:09 316 查看


【引言】

经常在迭代集合元素时,会想对集合做修改(add/remove)操作,类似下面这段代码:

[java] view
plaincopy

for (Iterator<Integer> it = list.iterator(); it.hasNext(); ) {

Integer val = it.next();

if (val == 5) {

list.remove(val);

}

}

运行这段代码,会抛出异常java.util.ConcurrentModificationException。


【解惑】

(以ArrayList来讲解)在ArrayList中,它的修改操作(add/remove)都会对modCount这个字段+1,modCount可以看作一个版本号,每次集合中的元素被修改后,都会+1(即使溢出)。接下来再看看AbsrtactList中iteraor方法

[java] view
plaincopy

public Iterator<E> iterator() {

return new Itr();

}

它返回一个内部类,这个类实现了iterator接口,代码如下:

[java] view
plaincopy

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 的值

expectedModCount = modCount;

} catch (IndexOutOfBoundsException e) {

throw new ConcurrentModificationException();

}

}

final void checkForComodification() {

if (modCount != expectedModCount)

throw new ConcurrentModificationException();

}

}

在内部类Itr中,有一个字段expectedModCount ,初始化时等于modCount,即当我们调用list.iterator()返回迭代器时,该字段被初始化为等于modCount。在类Itr中next/remove方法都有调用checkForComodification()方法,在该方法中检测modCount == expectedModCount,如果不相当则抛出异常ConcurrentModificationException。

前面说过,在集合的修改操作(add/remove)中,都对modCount进行了+1。

在看看刚开始提出的那段代码,在迭代过程中,执行list.remove(val),使得modCount+1,当下一次循环时,执行 it.next(),checkForComodification方法发现modCount != expectedModCount,则抛出异常。

【解决办法】

如果想要在迭代的过程中,执行删除元素操作怎么办?

再来看看内部类Itr的remove()方法,在删除元素后,有这么一句expectedModCount = modCount,同步修改expectedModCount 的值。所以,如果需要在使用迭代器迭代时,删除元素,可以使用迭代器提供的remove方法。对于add操作,则在整个迭代器迭代过程中是不允许的。 其他集合(Map/Set)使用迭代器迭代也是一样。



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: