简体   繁体   中英

HashMap throws ConcurrentModification even if using iterator.remove() to remove node

I've got a ConcurrentModificationException when operating a HashMap . The code is as following:

    Iterator<Integer> iterator =cacheMap.keySet().iterator();
        while(iterator.hasNext()) {
        if(iterator.next() == target) {
            iterator.remove();
        }
    }

The only operations I used were remove like that and cacheMap.put(node) . And the methods were all called in the main thread, including onCreate() method of Activity , onPostExecute() method of AsyncTask and handleMessage(Message msg) method in the handler of main thread.

But occasionally, ConcurrentModificationException was thrown in iterator.next() when I use the method above to remove a node, although it's really few.

I've reviewed the relevant method in HashMap. It's like this:

@Override public Set<K> keySet() {
    Set<K> ks = keySet;
    return (ks != null) ? ks : (keySet = new KeySet());
}

private final class KeyIterator extends HashIterator
        implements Iterator<K> {
    public K next() { return nextEntry().key; }
}

private abstract class HashIterator {
    int nextIndex;
    HashMapEntry<K, V> nextEntry = entryForNullKey;
    HashMapEntry<K, V> lastEntryReturned;
    int expectedModCount = modCount;

    HashIterator() {
        if (nextEntry == null) {
            HashMapEntry<K, V>[] tab = table;
            HashMapEntry<K, V> next = null;
            while (next == null && nextIndex < tab.length) {
                next = tab[nextIndex++];
            }
            nextEntry = next;
        }
    }

    public boolean hasNext() {
        return nextEntry != null;
    }

    HashMapEntry<K, V> nextEntry() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        if (nextEntry == null)
            throw new NoSuchElementException();

        HashMapEntry<K, V> entryToReturn = nextEntry;
        HashMapEntry<K, V>[] tab = table;
        HashMapEntry<K, V> next = entryToReturn.next;
        while (next == null && nextIndex < tab.length) {
            next = tab[nextIndex++];
        }
        nextEntry = next;
        return lastEntryReturned = entryToReturn;
    }

    public void remove() {
        if (lastEntryReturned == null)
            throw new IllegalStateException();
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        HashMap.this.remove(lastEntryReturned.key);
        lastEntryReturned = null;
        expectedModCount = modCount;
    }
}

@Override public V remove(Object key) {
    if (key == null) {
        return removeNullKey();
    }
    int hash = secondaryHash(key);
    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;
}

/**
 * Subclass overrides this method to unlink entry.
 */
void postRemove(HashMapEntry<K, V> e) { }

The modCount and the expectedModeCount is checked when calling nextEntry() and remove . But it seems impossible for the difference of the two integer if iterator.remove() is called to remove node.

Just use cacheMap.remove(target).

From the Docs:

public V remove(Object key) Removes the mapping for the specified key from this map if present.

And remember a HashMap can only store one object for a given key, so no need to iterate over all values.

you can not iterate a collection and remove items from it.

use:

Set<Integer> set = new HashSet<Integer>();
Iterator<Integer> iterator =cacheMap.keySet().iterator();
    while(iterator.hasNext()) {
        Integer key = iterator.next();
        if(key == target) {
            set.add(key );
        }
    }
    cacheMap.rmoveAll(set);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM