ConcurrentModificationException with LinkedHashMap

不确定当我循环访问下面的代码中的结构时触发了什么。使用该方法工作正常。从之前的帖子中没有得到关于触发这种情况的好解释。java.util.ConcurrentModificationExceptionLinkedHashMapMap.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();

    }

}

答案 1

阅读LinkedHashMap的Javadoc

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

由于您正在传入构造函数,因此它是按访问顺序排列的,当您尝试从中读取某些内容时,您正在从结构上对其进行修改。trueLinkedHashMapget

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

增强语句的格式为:for

EnhancedForStatement:

for ( TargetType Identifier : Expression ) Statement

[...]

如果表达式的类型是某个类型参数 的子类型,则设为类型 ;否则,设为 原始类型 。Iterable<X>XIjava.util.Iterator<X>Ijava.util.Iterator

增强语句等效于以下形式的基本语句:forfor

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

#i是自动生成的标识符,与在发生增强 for 语句时作用域 (§6.3) 中的任何其他标识符(自动生成或以其他方式)不同。

另外,在 Javadoc 中用于:LinkedHashMap

由所有此类的集合视图方法返回的集合的方法返回的迭代器都是失败快速的:如果在创建迭代器后的任何时候对映射进行了结构修改,则除了通过迭代器自己的方法之外,迭代器将抛出一个 .iteratorremoveConcurrentModificationException

因此,当您调用地图时,您正在对其执行结构修改,从而导致增强型 for 中的迭代器引发异常。我想你是想这样做的,这样可以避免打电话:getget

for (Integer i : lru_cache.values()) {
    System.out.println(i);
}

答案 2

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

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

简单地调用就足以被视为结构修改,从而触发异常。如果使用序列,则只查询条目而不是映射,因此不会触发 .getentrySet()ConcurrentModificationException


推荐