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
结构修改是添加或删除一个或多个映射的任何操作,或者在访问顺序链接的哈希映射的情况下,影响迭代顺序.在插入有序链接散列映射中,仅更改与已包含在映射中的键相关联的值不是结构修改.在访问顺序链接的哈希映射中,仅查询映射
get是结构修改.
由于您正在传递true给LinkedHashMap构造函数,因此它处于访问顺序中,当您尝试从中进行get某些操作时,您正在对其进行结构修改.
另请注意,使用增强for语法时,实际上是在使用迭代器.JLS§14.14.2简化引用:
增强的
for声明具有以下形式:Run Code Online (Sandbox Code Playgroud)EnhancedForStatement: for ( TargetType Identifier : Expression ) Statement[...]
如果Expression的类型是
Iterable<X>某个类型参数的子类型X,那么让它I成为类型java.util.Iterator<X>; 否则,让我们I成为原始类型java.util.Iterator.增强
for语句等同于for表单的基本语句:Run Code Online (Sandbox Code Playgroud)for (I #i = Expression.iterator(); #i.hasNext(); ) { TargetType Identifier = (TargetType) #i.next(); Statement }
#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)
您正在使用访问顺序链接哈希映射:来自http://docs.oracle.com/javase/7/docs/api/java/util/LinkedHashMap.html上的规范,
结构修改是添加或删除一个或多个映射的任何操作,或者在访问顺序链接的哈希映射的情况下,影响迭代顺序.在插入有序链接散列映射中,仅更改与已包含在映射中的键相关联的值不是结构修改.在访问顺序链接哈希映射中,仅使用get查询映射是一种结构修改.)
简单地调用get足以被视为结构修改,触发异常.如果你使用entrySet()序列,你只是查询条目而不是地图,所以你不要触发ConcurrentModificationException.
在构造函数中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。
| 归档时间: |
|
| 查看次数: |
10976 次 |
| 最近记录: |