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"); for(String str : list){ if(del.contains(str)) { list.remove(str); } } }
Exception in thread "main" java.util.ConcurrentModificationException
public Iterator<E> iterator() { return new 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();} }
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 } ?
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();} }
List<String> save = new ArrayList<String>(); for(String str : list) { if(del.contains(str)) { save.add(str); } } list.removeAll(save);?
Iterator<String> iterator = list.iterator(); while(iterator.hasNext()) { String str = iterator.next(); if(del.contains(str)) { iterator.remove(); } }
? ? ??Iterator.remove()方法保证了modCount和expectedModCount的值的一致性,避免抛出ConcurrentModificationException异常。
?
不过对于在多线程环境下对集合类元素进行迭代修改操作,最好把代码放在一个同步代码块内,这样才能保证modCount和expectedModCount的值的一致性,类似如下:
?
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();会维护modCount和expectedModCount的值的一致性,而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; }
private V removeNullKey() { HashMapEntry<K, V> e = entryForNullKey; if (e == null) { return null; } entryForNullKey = null; modCount++; size--; postRemove(e); return e.value; }
?