Java集合修改异常:ConcurrentModificationException with LinkedHashMap

集合修改异常:ConcurrentModificationException with LinkedHashMap

ref: https://stackoverflow.com/questions/16180568/concurrentmodificationexception-with-linkedhashmap

项目中并没有对集合进行修改操作,但是却报错了这个异常,甚是疑惑,所以了解了一下这个问题。

	java.util.ConcurrentModificationException
	#011at java.util.LinkedHashMap$LinkedHashIterator.nextEntry(LinkedHashMap.java:394)
	#011at java.util.LinkedHashMap$KeyIterator.next(LinkedHashMap.java:405)

访问顺序链接哈希映射中,仅使用get查询映射是一种结构修改

	protected boolean isUpperLimit() {
		Set<Long> keySet = upperLimitMap.keySet();
		int count = 0;
		for (Long time : keySet) {
			....
		}
	}

查看文档

ref: https://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.

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

所以上面的遍历是在访问顺序下,仅适用get的方式,属于一种结构修改。

In the constructor of LinkedHashMap you pass true to get the LRU behaviour (meaning the eviction policy is access order rather than false for insertion order).
So every time you call get(key) the underlying Map.Entry increments an access counter AND reorders the collection by moving the (last accessed) Map.Entry to the head of the list.
The iterator (implicitly created by the for loop) checks the modified flag, which is different from the copy it took originally, so throws the ConcurrentModificationException.

在LinkedHashMap的构造函数中,您传递true以获取LRU行为(意味着驱逐策略是访问顺序而不是参数为false的插入顺序)。
因此,每次调用get(key)时,底层Map.Entry都会增加一个访问计数器,并通过将(最后访问的)Map.Entry移动到列表的头部来重新排序集合。
迭代器(由for循环隐式创建)检查修改后的标志,该标志与最初使用的副本不同,因此抛出ConcurrentModificationException。

正确的做法

使用entrySet()

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.
To avoid this you should use the entrySet() as the implementation is inherited from java.util.HashMap and therefore the iterator doesn’t check the modification flags:

for(Map.Entry<String,Integer> e : lru_cache.entrySet()){
	System.out.println(e.getValue());
}
展开阅读全文

没有更多推荐了,返回首页