-->

ConcurrentModificationException的用的LinkedHashMap(Co

2019-08-31 23:59发布

不知道是什么触发一个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();

    }

}

Answer 1:

阅读的Javadoc中LinkedHashMap

结构上的修改是指添加或删除一个或多个映射,或者在访问顺序链接的哈希映射的情况下的任何操作,影响迭代顺序。 在插入顺序链接的哈希映射,仅改变与该已经包含在地图中的键相关联的值不是结构修改。 在访问顺序链接的哈希映射中,仅查询与地图get是一个结构性的改变。

既然你传递trueLinkedHashMap构造函数,它是在访问顺序,当你试图get从它的东西,你在结构上进行修改。

还要注意的是,当您使用增强for语法,你实际上是使用迭代器。 从简化的报价JLS§14.14.2 :

增强for语句的形式:

 EnhancedForStatement: for ( TargetType Identifier : Expression ) Statement 

[...]

如果表达的类型是其亚型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 } 

#i是自动生成的标识符,从该点在哪里for语句出现增强在范围(§6.3)的任何其他标识符(自动生成的或以其他方式)不同。

此外,在Javadoc中LinkedHashMap

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

因此,当你调用get在地图上,你给它进行结构修饰,导致在增强,对抛出异常的迭代器。 我觉得你的意思要做到这一点,这就避免了调用get

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


Answer 2:

您使用的访问顺序链接的哈希地图:从规范的http://docs.oracle.com/javase/7/docs/api/java/util/LinkedHashMap.html ,

结构上的修改是指添加或删除一个或多个映射,或者在访问顺序链接的哈希映射的情况下的任何操作,影响迭代顺序。 在插入顺序链接的哈希映射,仅改变与该已经包含在地图中的键相关联的值不是结构修改。 在访问顺序链接的哈希映射中,仅查询与获取地图是一个结构上的修改。)

简单地调用get足以被认为是结构上的修改,引发异常。 如果您使用entrySet()你只查询入口,而不是图序列,这样你就不会触发ConcurrentModificationException



Answer 3:

在构造函数中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());
    }

请注意这个类是不是在你需要使用一个潜在的昂贵后卫像Collections.synchronizedMap(图)并发环境线程安全等等。 在这种情况下一个更好的选择可能是谷歌的番石榴缓存 。



Answer 4:

java.util.ConcurrentModificationException :如果在迭代中存在有任何结构上的改变(添加,删除,重散列等),以下面的列表。 迭代器检查,看是否列表中的每个操作之前改变。 这被称为“无故障运行”。

如果一个线程直接修改的集合,同时它遍历了快速失败的迭代器集合,迭代器将抛出此exception.Here你不能调用get() ,而使用迭代器,因为调用方法get()从结构上修改地图并因此的迭代方法的一个的下一个呼叫失败并引发一个ConcurrentModificationException



Answer 5:

您的代码

for(String key : lru_cache.keySet()){
    System.out.println(lru_cache.get(key));
}

实际上编译到:

Iterator<String> it = lru_cache.keySet().iterator();
while (it.hasNext()) {
    String key = it.next();
    System.out.println(lru_cache.get(key));
}

接下来,你的LRU缓存缩减自身调用时MAX_SIZE元素不set()但调用时get() -以上的答案解释原因。

因此,我们有以下行为:

  • 创建遍历新的迭代lru_cache.keySet()集合
  • lru_cache.get()调用从缓存中提取元素
  • get()调用截断lru_cache到MAX_SIZE元素(在你的情况3)
  • 迭代器it由于收集的修改变得无效,并引发对下一次迭代。


Answer 6:

这是堂妹的集合框架也可以在修改列表(通过添加或删除元素)的快速失败行为,同时遍历列表,此错误将迭代器在那里。 我碰到这个错误来到了一段时间回来。 请参考下线程来查看详细信息。

在ArrayList的foreach循环里面当添加ConcurrentModificationException的

虽然这说数组列表,它适用于大部分的集合(或多个)数据strucutres的。

并发修改例外:添加到ArrayList

http://docs.oracle.com/javase/6/docs/api/java/util/ConcurrentModificationException.html



文章来源: ConcurrentModificationException with LinkedHashMap