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

在做leetcode349(求两集合的交集)debug时遇到了这样的异常。

        Iterator it = n1.iterator();
        while (it.hasNext()) {
            Object x = it.next();
            if(n2.contains(x) == false) {
               n1.remove(x);
            }
        }        

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

        for(Integer e: n1) {
            if(n2.contains(e) == false) {
                n1.remove(e);
            }
        }        

其实map或set都会这样。

解决办法为:如果不是Iterator迭代方式,则修改map迭代方式为Iterator()方式,采用iterator.remove();而不直接通过map.remove();

        Iterator it = n1.iterator();
        while (it.hasNext()) {
            Object x = it.next();
            if(n2.contains(x) == false) {
                it.remove();
            }
        }

详细原因如下:

发现这个位置应该是不会报错的,查找前后文,发现最有可能报错的应该是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();
        }
    }
    }
    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同样自增。也就不会抛出异常。

猜你喜欢

转载自www.cnblogs.com/wangtinglou/p/9424425.html