java.util.ConcurrentModificationException详解

java.util.ConcurrentModificationException详解

http://blog.csdn.net/smcwwh/article/details/7036663

 

目录(?)[+]

 

【引言】

经常在迭代集合元素时,会想对集合做修改(add/remove)操作,类似下面这段代码:
[java] view plain copy
 
  1. for (Iterator<Integer> it = list.iterator(); it.hasNext(); ) {  
  2.     Integer val = it.next();  
  3.     if (val == 5) {  
  4.         list.remove(val);  
  5.     }  
  6. }  

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

【解惑】

(以ArrayList来讲解)在ArrayList中,它的修改操作(add/remove)都会对modCount这个字段+1,modCount可以看作一个版本号,每次集合中的元素被修改后,都会+1(即使溢出)。接下来再看看AbsrtactList中iteraor方法
[java] view plain copy
 
  1. public Iterator<E> iterator() {  
  2.     return new Itr();  
  3. }  

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

[java] view plain copy
 
  1. private class Itr implements Iterator<E> {  
  2.     int cursor = 0;  
  3.   
  4.     int lastRet = -1;  
  5.   
  6.     int expectedModCount = modCount;  
  7.   
  8.     public boolean hasNext() {  
  9.         return cursor != size();  
  10.     }  
  11.   
  12.     public E next() {  
  13.         checkForComodification();  
  14.         try {  
  15.             E next = get(cursor);  
  16.             lastRet = cursor++;  
  17.             return next;  
  18.         } catch (IndexOutOfBoundsException e) {  
  19.             checkForComodification();  
  20.             throw new NoSuchElementException();  
  21.         }  
  22.     }  
  23.   
  24.     public void remove() {  
  25.         if (lastRet == -1)  
  26.             throw new IllegalStateException();  
  27.         checkForComodification();  
  28.   
  29.         try {  
  30.             AbstractList.this.remove(lastRet);  
  31.             if (lastRet < cursor)  
  32.                 cursor--;  
  33.             lastRet = -1;  
  34.             // 修改expectedModCount 的值  
  35.             expectedModCount = modCount;  
  36.             } catch (IndexOutOfBoundsException e) {  
  37.             throw new ConcurrentModificationException();  
  38.         }  
  39.     }  
  40.   
  41.     final void checkForComodification() {  
  42.         if (modCount != expectedModCount)  
  43.             throw new ConcurrentModificationException();  
  44.     }  
  45.     }  

在内部类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)使用迭代器迭代也是一样。