在日常的开发中,我们经常需要对map,list等容器进行移除,但是处理不小心就会抛出ConcurrentModificationException异常,这到底是什么原因造成的以及如何避免?这个本博文分析的重点。
首先看一下这个map的遍历程序
package test1;
import java.util.HashMap;
import java.util.Map;
public class test {
public static void main(String[] args){
Map<Integer,Integer> map=new HashMap<>();
//初始化map
for(int i=0;i<10;i++){
map.put(i,i);
}
for(Map.Entry<Integer,Integer> entry:map.entrySet()){
if(entry.getKey()==1){
map.remove(entry.getKey());
}
System.out.println(entry.getKey()+" "+entry.getValue());
}
}
}
运行的时候会抛出ConcurrentModificationException:
这是什么原因造成的呐?下面我们来分析一下源码
首先HashMap里面有个成员属性,如下图
这个成员属性是记录map一共被修改的次数的,也就是对map进行put,remove的时候,这个modCount也是需要增加次数的。
而当我们使用迭代器Iterator对map进行遍历的时候,在迭代器创建的时候就将这个值赋予给了迭代器里面的一个成员变量expectedModCount。
然后我们再来看看map.remove()方法发生了什么?
问题就出在这里,map.remove()方法只会修改modCount成员变量,执行一次remove()方法后modCount变量加1,而expectedModCount还是原来的值。
因此在下一次迭代器执行next()方法的时候 ,就会因为modeCount和expectedModCount这两个不一致而抛出错误。
所以总结一下,凡是用迭代器遍历并用map.remove()移除的都会出现这个问题。
那么,日常开发中,我们想在迭代中移除元素,那该怎么办呢?解决办法很简单,代码修改如下:
package test1;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class test {
public static void main(String[] args){
Map<Integer,Integer> map=new HashMap<>();
//初始化map
for(int i=0;i<10;i++){
map.put(i,i);
}
Iterator<Map.Entry<Integer,Integer>> iterator=map.entrySet().iterator();
while (iterator.hasNext()){
Map.Entry<Integer, Integer> next=iterator.next();
int key=next.getKey();
int value=next.getValue();
if(key==1){
iterator.remove();
}
System.out.println(key+" "+value);
}
}
}
只要用迭代器中的remove()方法删除元素就行了,那我们再来看看迭代器中的remove()方法做了什么。
可以看到每一次remove,都会expectedModCount同步一下modCount,这就不会造成两者数值不相等啦。
重点来了,那为什么java要这样设计呢?是不是大牛们吃饱了撑着了呢?想什么呐,肯定不是噶
原因如下:HashMap和keySet的remove方法都可以通过传递key参数删除任意的元素,而iterator只能删除当前元素(current),一
旦删除的元素是iterator对象中next所正在引用的,如果没有通过modCount、 expectedModCount的比较实现快速失败抛出异
常,下次循环该元素将成为current指向,此时iterator就遍历了一个已移除的过期数据。