与LinkedHashMap的ConcurrentModificationException

Adi*_*uja 15 java linkedhashmap concurrentmodification

java.util.ConcurrentModificationException当我LinkedHashMap在下面的代码中迭代结构时,不确定触发的是什么.使用这种Map.Entry方法很好.没有从之前的帖子中得到关于触发这个问题的一个很好的解释.

任何帮助,将不胜感激.

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();

    }

}
Run Code Online (Sandbox Code Playgroud)

gpa*_*ani 41

阅读JavadocLinkedHashMap:

结构修改是添加或删除一个或多个映射的任何操作,或者在访问顺序链接的哈希映射的情况下,影响迭代顺序.在插入有序链接散列映射中,仅更改与已包含在映射中的键相关联的值不是结构修改.在访问顺序链接的哈希映射中,仅查询映射get是结构修改.

由于您正在传递trueLinkedHashMap构造函数,因此它处于访问顺序中,当您尝试从中进行get某些操作时,您正在对其进行结构修改.

另请注意,使用增强for语法时,实际上是在使用迭代器.JLS§14.14.2简化引用:

增强的for声明具有以下形式:

EnhancedForStatement:

for ( TargetType Identifier : Expression ) Statement
Run Code Online (Sandbox Code Playgroud)

[...]

如果Expression的类型是Iterable<X>某个类型参数的子类型X,那么让它I成为类型java.util.Iterator<X>; 否则,让我们I成为原始类型java.util.Iterator.

增强for语句等同于for表单的基本语句:

for (I #i = Expression.iterator(); #i.hasNext(); ) {
     TargetType Identifier =
         (TargetType) #i.next();
     Statement
}
Run Code Online (Sandbox Code Playgroud)

#i 是一个自动生成的标识符,它与发生增强for语句时的范围(第6.3节)中的任何其他标识符(自动生成的或其他标识符)不同.

另外,在Javadoc中LinkedHashMap:

在返回的迭代器iterator通过所有这些类的集合视图方法返回的集合的方法是 快速失败的:如果地图在任何时间,通过迭代器自身的结构修饰在创建迭代器之后,以任何方式除了 remove方法,迭代器会抛出一个 ConcurrentModificationException.

因此,当您get在地图上调用时,您正在对其执行结构修改,导致enhanced-for中的迭代器抛出异常.我认为你打算这样做,这避免了调用get:

for (Integer i : lru_cache.values()) {
    System.out.println(i);
}
Run Code Online (Sandbox Code Playgroud)


Fem*_*emi 8

您正在使用访问顺序链接哈希映射:来自http://docs.oracle.com/javase/7/docs/api/java/util/LinkedHashMap.html上的规范,

结构修改是添加或删除一个或多个映射的任何操作,或者在访问顺序链接的哈希映射的情况下,影响迭代顺序.在插入有序链接散列映射中,仅更改与已包含在映射中的键相关联的值不是结构修改.在访问顺序链接哈希映射中,仅使用get查询映射是一种结构修改.)

简单地调用get足以被视为结构修改,触发异常.如果你使用entrySet()序列,你只是查询条目而不是地图,所以你不要触发ConcurrentModificationException.


ear*_*cam 6

在构造函数中LinkedHashMap传递true,以获得LRU行为(指驱逐策略是访问顺序,而不是false用于插入顺序)。

因此,每次调用get(key)底层 Map.Entry 时,都会增加一个访问计数器,并通过将(上次访问的)Map.Entry 移动到列表的头部来重新排序集合。

迭代器(由 for 循环隐式创建)检查修改后的标志,这与它最初采用的副本不同,因此抛出ConcurrentModificationException.

为了避免这种情况,您应该使用 ,entrySet()因为实现是从 java.util.HashMap 继承的,因此迭代器不会检查修改标志:

    for(Map.Entry<String,Integer> e : lru_cache.entrySet()){
        System.out.println(e.getValue());
    }
Run Code Online (Sandbox Code Playgroud)

请注意,此类不是线程安全的,因此在并发环境中,您将需要使用可能昂贵的守卫,例如 Collections.synchronizedMap(Map)。在这种情况下,更好的选择可能是Google 的 Guava Cache