前几天在写程序过程中,需要根据判断条件删除一个Map中的相应数据,我自然而然想到可以通过调用Map中的remove(Object key)函数进行删除:代码如下:
public void processMap(Map list) {
Map map = list;
Iterator iter = map.keyset().iterator;
while(iter.hasNext()) {
double key = iter.next();
if (key > 5){
map.remove(key);
}
}
}
但是运行程序的时候却没有正常删除元素,而是提示“Java.util.ConcurrentModificationException”错误,我想起了之前见到过这个错误的解释,好像是迭代器有问题了,但一时半会没记起怎么解决。
于是我曲线救国,在 map.remove(key);后加上iter = map.keyset().iterator;也就是重新迭代,结果,不报错了,但是数据好像是不对。
很是疑惑,于是我断点调试,明白了,map中的数据又重新从第一个迭代了。好吧,逻辑不对。百度一下,找到了原因和解决方案。我把人家的原文修改了一下格式,并对代码进行修正。
找了一些关于Map的资料发现:Map的实现不是同步的。
具体是:
1.如果程序中出现多个线程同时访问一个Map,而其中至少一个线程修改Map时,它必须保持外部同步。
2.而通过查看Iterator原理发现,Iterator是工作在一个独立的线程中,并且拥有一个 mutex锁,就是说Iterator在工作的时候,是不允许被迭代的对象被改变的,所以调用Iterator操作获得的对象在多线程修改Map的时候会自动失效。
3.Iterator被创建的时候,建立了一个内存索引表(单链表),这 个索引表指向原来的对象,当原来的对象数量改变的时候,这个索
引表的内容没有同步改变,所以当索引指针往下移动的时候,便找不到要迭代的对象,于是产生错 误。
4.Map、List、Set等是动态的,可变对象数量的数据结构,但是Iterator则是单向不可变,只能顺序读取,不能逆序操作的数据结构,当 Iterator指向的原始
数据发生变化时,Iterator自己就迷失了方向。
找到了问题的原因,那么如何解决呢?可以通过调用Iterator的remove()函数来移除元素。
测试代码如下:
public void processMap(Map list) {
Map map = list;
Iterator iter = map.keyset().iterator;
while(iter.hasNext()) {
double key = iter.next();
if (key > 5) {
// map.remove(key); // java.util.ConcurrentModificationException
iter.remove(); // OK
}
}
}
同时,在遍历Map过程中,调用put(key, value)函数来添加元素,也会出现同样的问题,所以同样需要使用迭代器的相应函数来添加。
声明:本文内容绝大部分复制的别人的,原因一是原文格式和代码均不太易于观看,二是自己备份一下,便于翻阅。