android java.util.ConcurrentModificationException

android  java.util.ConcurrentModificationException

 1.  使用 java.util.concurrent 包下的集合

      java.util.concurrent.ConcurrentHashMap;

      java.util.concurrent.CopyOnWriteArrayList;

      Please refer to http://www.javacodegeeks.com/2011/05/avoid-concurrentmodificationexception.html 

 

2. 循环 list or map 时 使用 iterator 来删除被循环的元素,或者将要删除或者增加的 元素先保存在一个临时集合里,待循环结束后再一次性操作,参考:  http://zhoujianghai.iteye.com/blog/1041555 

 

以下内容转发自:  http://zhoujianghai.iteye.com/blog/1041555 

 

用iterator遍历集合时碰到java.util.ConcurrentModificationException这个异常,

下面以List为例来解释为什么会报java.util.ConcurrentModificationException这个异常,代码如下:

public static void main(String[] args) {  
 List<String> list = new ArrayList<String>();  
         list.add("1");  
          list.add("2");  
          list.add("3");  
          list.add("4");  
          list.add("5");  
          list.add("6");  
          list.add("7");  
            
 List<String> del = new ArrayList<String>();  
          del.add("5");  
          del.add("6");  
          del.add("7");    
  
<span style="color: #ff0000;">for(String str : list){  
     if(del.contains(str)) {  
            list.remove(str);            
}  
          }</span>  
 }  

运行这段代码会出现如下异常:Exception in thread "main" java.util.ConcurrentModificationException 

 for(String str : list) 这句话实际上是用到了集合的iterator() 方法

 JDK java.util. AbstractList类中相关源码

public Iterator<E> iterator() {  

  1.    return new Itr();  
  2. }  
java.util.  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;  
  
    public boolean hasNext() {  
            return cursor != size();  
    }  
  
    public E next() {  
            checkForComodification(); //检测modCount和expectedModCount的值!!  
        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); //执行remove的操作  
        if (lastRet < cursor)  
            cursor--;  
        lastRet = -1;  
        expectedModCount = modCount; //保证了modCount和expectedModCount的值的一致性,避免抛出ConcurrentModificationException异常  
        } catch (IndexOutOfBoundsException e) {  
        throw new ConcurrentModificationException();  
        }  
    }  
  
    final void checkForComodification() {  
        if (modCount != expectedModCount) //当modCount和expectedModCount值不相等时,则抛出ConcurrentModificationException异常  
        throw new ConcurrentModificationException();  
    }  
    }  
再看一下 ArrayList  的  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;  
    }  
  
    /* 
     * Private remove method that skips bounds checking and does not 
     * return the value removed. 
     */  
    private void fastRemove(int index) {  
        modCount++; //只是修改了modCount,因此modCount将与expectedModCount的值不一致  
        int numMoved = size - index - 1;  
        if (numMoved > 0)  
            System.arraycopy(elementData, index+1, elementData, index,  
                             numMoved);  
        elementData[--size] = null; // Let gc do its work  
    }   

回过头去看看 java.util.  AbstractList next() 方法
public E next() {  
            checkForComodification(); //检测modCount和expectedModCount的值!!  
        try {  
        E next = get(cursor);  
        lastRet = cursor++;  
        return next;  
        } catch (IndexOutOfBoundsException e) {  
        checkForComodification();  
        throw new NoSuchElementException();  
        }  
    }  
   
final void checkForComodification() {  
        if (modCount != expectedModCount) //当modCount和expectedModCount值不相等时,则抛出ConcurrentModificationException异常  
        throw new ConcurrentModificationException();  
    }  
    }  

现在真相终于大白了,ArrayList的remove方法只是修改了modCount的值,并没有修改expectedModCount,导致modCount和expectedModCount的值的不一致性,当next()时则抛出ConcurrentModificationException异常

因此使用Iterator遍历集合时,不要改动被迭代的对象,可以使用 Iterator 本身的方法 remove() 来删除对象,Iterator.remove() 方法会在删除当前迭代对象的同时维护modCount和expectedModCount值的一致性。

 解决办法如下:

(1)  新建一个集合存放要删除的对象,等遍历完后,调用removeAll(Collection<?> c)方法

把上面例子中迭代集合的代码替换成:

List<String> save = new ArrayList<String>();  
            
          for(String str : list)  
          {  
           if(del.contains(str))  
           {  
               save.add(str);  
           }  
          }  
          list.removeAll(save);  

 (2)  使用 Iterator 替代增强型 for 循环:
Iterator<String> iterator = list.iterator();  
     while(iterator.hasNext()) {  
         String str = iterator.next();  
         if(del.contains(str)) {  
             iterator.remove();  
         }  
     }  

  Iterator.remove()方法保证了modCountexpectedModCount的值的一致性,避免抛出ConcurrentModificationException异常。

 不过对于在多线程环境下对集合类元素进行迭代修改操作,最好把代码放在一个同步代码块内,这样才能保证modCountexpectedModCount的值的一致性,类似如下:

Iterator<String> iterator = list.iterator();    
synchronized(synObject) {  
 while(iterator.hasNext()) {    
         String str = iterator.next();    
         if(del.contains(str)) {    
             iterator.remove();    
         }    
     }    
}  
因为迭代器实现类如:ListItr的next(),previous(),remove(),set(E e),add(E e)这些方法都会调用checkForComodification(),源码:
final void checkForComodification() {  
        if (modCount != expectedModCount)  
        throw new ConcurrentModificationException();  
    }  
曾经写了下面这段对HashMap进行迭代删除操作的错误的代码:
Iterator<Integer> iterator = windows.keySet().iterator();  
        while(iterator.hasNext()) {  
            int type = iterator.next();  
            windows.get(type).closeWindow();  
            iterator.remove();  
            windows.remove(type);   //  
        }  

 上面的代码也会导致ConcurrentModificationException的发生。罪魁祸首是windows.remove(type);这一句。

根据上面的分析我们知道iterator.remove();会维护modCountexpectedModCount的值的一致性,而windows.remove(type);这句是不会的。其实这句是多余的,上面的代码去掉这句就行了。

iterator.remove()的源码如下:HashIterator类的remove()方法

public void remove() {  
            if (lastEntryReturned == null)  
                throw new IllegalStateException();  
            if (modCount != expectedModCount)  
                throw new ConcurrentModificationException();  
            HashMap.this.remove(lastEntryReturned.key);  
            lastEntryReturned = null;  
            expectedModCount = modCount; //保证了这两值的一致性  
        }  

 HashMap.this.remove(lastEntryReturned.key);这句代码说明windows.remove(type);是多余的,因为已经删除了该key对应的value。

windows.remove(type)的源码:

 public V remove(Object key) {  

        if (key == null) {  
            return removeNullKey();  
        }  
        int hash = secondaryHash(key.hashCode());  
        HashMapEntry<K, V>[] tab = table;  
        int index = hash & (tab.length - 1);  
        for (HashMapEntry<K, V> e = tab[index], prev = null;  
                e != null; prev = e, e = e.next) {  
            if (e.hash == hash && key.equals(e.key)) {  
                if (prev == null) {  
                    tab[index] = e.next;  
                } else {  
                    prev.next = e.next;  
                }  
                modCount++;  
                size--;  
                postRemove(e);  
                return e.value;  
            }  
        }  
        return null;  
    }  

 上面的代码中,由于先调用了iterator.remove();所以再调用HashMap的remove方法时,key就已经为null了,所以会执行:removeNullKey();

方法,removeNullKey()源码:

private V removeNullKey() {  
       HashMapEntry<K, V> e = entryForNullKey;  
       if (e == null) {  
           return null;  
       }  
       entryForNullKey = null;  
       modCount++;  
       size--;  
       postRemove(e);  
       return e.value;  
   }  

不过不管是执行removeNullKey()还是key != null,如果直接调用HashMap的remove方法,都会导致ConcurrentModificationException

这个异常的发生,因为它对modCount++;没有改变expectedModCount的值,没有维护维护索引的一致性。 

下面引用一段更专业的解释:

Iterator 是工作在一个独立的线程中,并且拥有一个 mutex 锁。 Iterator 被创建之后会建立一个指向原来对象的单链索引表,当原来的对象数量发生变化时,这个索引表的内容不会同步改变,所以当索引指针往后移动的时候就找不到要迭代的对象,所以按照 fail-fast 原则 Iterator 会马上抛出 java.util.ConcurrentModificationException 异常。
所以 Iterator 在工作的时候是不允许被迭代的对象被改变的。但你可以使用 Iterator 本身的方法 remove() 来删除对象, Iterator.remove() 方法会在删除当前迭代对象的同时维护索引的一致性。









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值