Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ConcurrentModificationException with LinkedHashMap

Not sure what is triggering a java.util.ConcurrentModificationException when I iterate over the LinkedHashMap structure in the code below. Using the Map.Entry approach works fine. Did not get a good explanation on what is triggering this from the previous posts.

Any help would be appreciated.

import java.util.LinkedHashMap;
import java.util.Map;

public class LRU {

    // private Map<String,Integer> m = new HashMap<String,Integer>();
    // private SortedMap<String,Integer> lru_cache = Collections.synchronizedSortedMap(new TreeMap<String, Integer>());

    private static final int MAX_SIZE = 3;

    private LinkedHashMap<String,Integer> lru_cache = new LinkedHashMap<String,Integer>(MAX_SIZE, 0.1F, true){
        @Override
        protected boolean removeEldestEntry(Map.Entry eldest) {
            return(lru_cache.size() > MAX_SIZE);
         }
    };    

    public Integer get1(String s){
        return lru_cache.get(s);        
    }

    public void displayMap(){
        /**
         * Exception in thread "main" java.util.ConcurrentModificationException
            at java.util.LinkedHashMap$LinkedHashIterator.nextEntry(LinkedHashMap.java:373)
            at java.util.LinkedHashMap$KeyIterator.next(LinkedHashMap.java:384)
            at LRU.displayMap(LRU.java:23)
            at LRU.main(LRU.java:47)
         */
        *for(String key : lru_cache.keySet()){
            System.out.println(lru_cache.get(key));
        }*

// This parser works fine        
//        for(Map.Entry<String, Integer> kv : lru_cache.entrySet()){
//            System.out.println(kv.getKey() + ":" + kv.getValue());
//        }
    }

    public void set(String s, Integer val){
        if(lru_cache.containsKey(s)){            
            lru_cache.put(s, get1(s) + val);
        }
        else{
            lru_cache.put(s, val);
        }
    }

    public static void main(String[] args) {

        LRU lru = new LRU();
        lru.set("Di", 1);
        lru.set("Da", 1);
        lru.set("Daa", 1);
        lru.set("Di", 1);        
        lru.set("Di", 1);
        lru.set("Daa", 2);
        lru.set("Doo", 2);
        lru.set("Doo", 1);        
        lru.set("Sa", 2);
        lru.set("Na", 1);
        lru.set("Di", 1);
        lru.set("Daa", 1);

        lru.displayMap();

    }

}
like image 980
Aditya Sakhuja Avatar asked Apr 23 '13 23:04

Aditya Sakhuja


People also ask

How do I fix ConcurrentModificationException in Java?

How do you fix Java's ConcurrentModificationException? There are two basic approaches: Do not make any changes to a collection while an Iterator loops through it. If you can't stop the underlying collection from being modified during iteration, create a clone of the target data structure and iterate through the clone.

Can Concurrenthashmap throws ConcurrentModificationException?

They do not throw ConcurrentModificationException.

How can we avoid ConcurrentModificationException in a single threaded environment?

We can also avoid the Concurrent Modification Exception in a single threaded environment. We can use the remove() method of Iterator to remove the object from the underlying collection object. But in this case, you can remove only the same object and not any other object from the list.


2 Answers

Read the Javadoc for LinkedHashMap:

A structural modification is any operation that adds or deletes one or more mappings or, in the case of access-ordered linked hash maps, affects iteration order. In insertion-ordered linked hash maps, merely changing the value associated with a key that is already contained in the map is not a structural modification. In access-ordered linked hash maps, merely querying the map with get is a structural modification.

Since you're passing in true to the LinkedHashMap constructor, it is in access order and when you are trying to get something from it, you are structurally modifying it.

Also note that when you use the enhanced for syntax, you are actually using an iterator. Simplified quote from JLS §14.14.2:

The enhanced for statement has the form:

EnhancedForStatement:

for ( TargetType Identifier : Expression ) Statement

[...]

If the type of Expression is a subtype of Iterable<X> for some type argument X, then let I be the type java.util.Iterator<X>; otherwise, let I be the raw type java.util.Iterator.

The enhanced for statement is equivalent to a basic for statement of the form:

for (I #i = Expression.iterator(); #i.hasNext(); ) {
     TargetType Identifier =
         (TargetType) #i.next();
     Statement
}

#i is an automatically generated identifier that is distinct from any other identifiers (automatically generated or otherwise) that are in scope (§6.3) at the point where the enhanced for statement occurs.

Also, in the Javadoc for LinkedHashMap:

The iterators returned by the iterator method of the collections returned by all of this class's collection view methods are fail-fast: if the map is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove method, the iterator will throw a ConcurrentModificationException.

Therefore, when you are calling get on the map, you are performing structural modifications to it, causing the iterator in the enhanced-for to throw an exception. I think you meant to do this, which avoids calling get:

for (Integer i : lru_cache.values()) {
    System.out.println(i);
}
like image 88
gparyani Avatar answered Oct 05 '22 23:10

gparyani


You're using an access-ordered linked hash map: from the spec at http://docs.oracle.com/javase/7/docs/api/java/util/LinkedHashMap.html,

A structural modification is any operation that adds or deletes one or more mappings or, in the case of access-ordered linked hash maps, affects iteration order. In insertion-ordered linked hash maps, merely changing the value associated with a key that is already contained in the map is not a structural modification. In access-ordered linked hash maps, merely querying the map with get is a structural modification.)

Simply calling get is enough to be considered a structural modification, triggering the exception. If you use the entrySet() sequence you're only querying the entry and NOT the map, so you don't trigger the ConcurrentModificationException.

like image 27
Femi Avatar answered Oct 05 '22 22:10

Femi