java.util.ConcurrentModificationException 异常解决的方法及原理

近期在修程序的bug,发现后台抛出下面异常:

Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.HashMap$HashIterator.nextEntry(HashMap.java:793)
	at java.util.HashMap$KeyIterator.next(HashMap.java:828)
	at com.keyman.demo.test.ClearResultTable.method2(ClearResultTable.java:54)
	at com.keyman.demo.test.ClearResultTable.main(ClearResultTable.java:88)

找到报错行:at com.keyman.demo.test.ClearResultTable.method2(ClearResultTable.java:54)发现,报错位置:for (String s1 : sets)

		Set<String> sets = map.keySet();
		for (String s1 : sets) {
			String value = map.get(s1);
			// 删除满足value以abc开头的键值对
			if (value.startsWith("abc")) {
				map.remove(s1);
			}
		}

或者以下的方式相同也会抛出异常

		Iterator<String> iterator = map.keySet().iterator();
		while (iterator.hasNext()) {
			String key = iterator.next();
			String value = map.get(key);
			// 删除满足value以abc开头的键值对
			if (value.startsWith("abc")) {
				map.remove(key); 
				//iterator.remove(); // 同步modCount和expectedModCount
				
			}
		}
事实上无论是Map还是Set这样操作时均会抛出此异常!

解决的方法为:假设不是Iterator迭代方式,则改动map迭代方式为Iterator()方式。採用iterator.remove();而不直接通过map.remove();

                Iterator<String> iterator = map.keySet().iterator();
		while (iterator.hasNext()) {
			String key = iterator.next();
			String value = map.get(key);
			// 删除满足value以abc开头的键值对
			if (value.startsWith("abc")) {
				//map.remove(value); 
				iterator.remove();   // 关键代码,同步modCount和expectedModCount
				
			}
		}

具体原因例如以下:

发现这个位置应该是不会报错的。查找前后文,发现最有可能报错的应该是for循环里面,可是咋一看压根没错!通过查找资料发现:当改动的个数跟期望改动的个数不相等时抛出此异常。

        private abstract class HashIterator<E> implements Iterator<E> {
	Entry<K, V> next; // next entry to return
	int expectedModCount; // For fast-fail
	int index; // current slot
	Entry<K, V> current; // current entry
	...
	final Entry<K, V> nextEntry() {
		if (modCount != expectedModCount)
			throw new ConcurrentModificationException(); // 抛出异常
		Entry<K, V> e = current = next;
		if (e == null)
			throw new NoSuchElementException();

		if ((next = e.next) == null) {
			Entry[] t = table;
			while (index < t.length && (next = t[index++]) == null)
				;
		}
		return e;
	}
	...
}

于是查看HashMap.remove()方法代码例如以下:

    /**
     * Removes the mapping for the specified key from this map if present.
     *
     * @param  key key whose mapping is to be removed from the map
     * @return the previous value associated with <tt>key</tt>, or
     *         <tt>null</tt> if there was no mapping for <tt>key</tt>.
     *         (A <tt>null</tt> return can also indicate that the map
     *         previously associated <tt>null</tt> with <tt>key</tt>.)
     */
    public V remove(Object key) {
        Entry<K,V> e = removeEntryForKey(key);
        return (e == null ? null : e.value);
    }

    /**
     * Removes and returns the entry associated with the specified key
     * in the HashMap.  Returns null if the HashMap contains no mapping
     * for this key.
     */
    final Entry<K,V> removeEntryForKey(Object key) {
        int hash = (key == null) ? 0 : hash(key.hashCode());
        int i = indexFor(hash, table.length);
        Entry<K,V> prev = table[i];
        Entry<K,V> e = prev;

        while (e != null) {
            Entry<K,V> next = e.next;
            Object k;
            if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k)))) {
                modCount++;
                size--;
                if (prev == e)
                    table[i] = next;
                else
                    prev.next = next;
                e.recordRemoval(this);
                return e;
            }
            prev = e;
            e = next;
        }

        return e;
    }
你会发现,当中有modCount++操作。modCount表示改动的次数。而并没有改变其exceptedmodCount;

接下来看看iterator.remove()方法:(java.util.Hashtable.Enumerator.remove())

	public void remove() {
	    if (!iterator)
		throw new UnsupportedOperationException();
	    if (lastReturned == null)
		throw new IllegalStateException("Hashtable Enumerator");
	    if (modCount != expectedModCount)
		throw new ConcurrentModificationException();

	    synchronized(Hashtable.this) {
		Entry[] tab = Hashtable.this.table;
		int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length;

		for (Entry<K,V> e = tab[index], prev = null; e != null;
		     prev = e, e = e.next) {
		    if (e == lastReturned) {
			modCount++;
			expectedModCount++;
			if (prev == null)
			    tab[index] = e.next;
			else
			    prev.next = e.next;
			count--;
			lastReturned = null;
			return;
		    }
		}
		throw new ConcurrentModificationException();
	    }
	}
    }
而此删除元素的方法,将modCount自增的同一时候将exceptedModCount相同自增。也就不会抛出异常。


原文地址:https://www.cnblogs.com/brucemengbm/p/6945602.html