笔者最近在调试项目bug的时候,遇到了一个很奇怪的bug,就是在对hashmap集合进行遍历的时候,同时作了remove操做,这个操做最后致使抛出了java.util.ConcurrentModificationException的错误。
带着疑惑,下面参考着源码,分析问题的缘由。
首先,重现问题,构造一个map并往里面加元素:java
private static HashMap<Integer, String> map = new HashMap<Integer, String>();;
public static void main(String[] args) {
for(int i = 0; i < 10; i++){
map.put(i, "value" + i);
}
}
复制代码
而后移除一些元素,此时就会报java.util.ConcurrentModificationException错误bash
for(Map.Entry<Integer, String> entry : map.entrySet()){
Integer key = entry.getKey();
if(key % 2 == 0){
System.out.println("To delete key " + key);
map.remove(key);
System.out.println("The key " + + key + " was deleted");
}
复制代码
从报错中能够看出,HashMap$HashIterator.nextNode这个方法有代码错误了,点进去看,大概知道HashMap.this.modCount != this.expectedModCount 成立并发
再看一下hashmap的remove操做是作了什么:高并发
这里对modCount进行了自增操做,表示操做动做+1。再看modCount和expectedModCount是什么东西this
能够看出迭代器初始化的时候就对modCount和expectedModCount进行同步。
到此,能够看出报错的缘由:spa
那什么状况下在遍历的时候能够删除map里面的元素呢?看下迭代器提供的remove方法:线程
能够看出迭代器里remove了一个元素以后会对expectedModCount从新赋值,这样再次遍历的时候就不会报错了。因此以前的代码能够改为以下写法,直接调用迭代器的remove方法。调试
Iterator<Map.Entry<Integer, String>> it = map.entrySet().iterator();
while(it.hasNext()){
Map.Entry<Integer, String> entry = it.next();
Integer key = entry.getKey();
if(key % 2 == 0){
System.out.println("To delete key " + key);
it.remove();
System.out.println("The key " + + key + " was deleted");
}
}
复制代码
替换机制:code