作者:cndz 围观群众:659 更新于 标签:map遍历java
在 Java 中,通常不能在遍历 Map 的过程中直接进行增删操作,这是因为会引发并发修改异常(ConcurrentModificationException)。以前在写代码的时候也知道不能在遍历Map的时候不能直接对map进行put、remove操作。但是一直没有探索原因,就想着今天好好研究一下,加深记忆防止踩坑吧。
public static void main(String[] args) {
Map<Integer, String> map = new HashMap<>();
map.put(1, "1");
map.put(2, "2");
map.put(3, "3");
for(Map.Entry<Integer,String> entry : map.entrySet()){
int key = entry.getKey();
if(1 == key){
map.remove(1);
}
String value = entry.getValue();
System.out.println(key + ":" + value);
}
}
引发异常错误信息:
ConcurrentModificationException 是 Java 集合框架的一种异常,它指示在遍历集合期间,集合的结构被修改了。当你调用 Map 的 iterator() 方法来进行遍历时,会创建一个迭代器来遍历 Map 的元素。
为什么会发生这种异常呢?这是因为迭代器在初始化时会记录 Map 的修改次数,每次进行增删操作时,修改次数都会增加。当修改次数与迭代器记录的次数不一致时,就会抛出异常。
在看源码的时候就会发现,我们使用Iterator进行遍历是都会用到下面这个方法。
final Node<K,V> nextNode() {
Node<K,V>[] t;
Node<K,V> e = next;
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (e == null)
throw new NoSuchElementException();
if ((next = (current = e).next) == null && (t = table) != null) {
do {} while (index < t.length && (next = t[index++]) == null);
}
return e;
}
上面的modCount是表示map中的元素被修改了几次(在移除,新加元素时此值都会自增),而expectedModCount是表示期望的修改次数,在迭代器构造的时候这两个值是相等,如果在遍历过程中这两个值出现了不同步就会抛出ConcurrentModificationException异常。
(1)HashMap本身的remove实现:
public V remove(Object key) {
Node<K,V> e;
return (e = removeNode(hash(key), key, null, false, true)) == null ? null : e.value;
}
(2)HashMap.KeySet的remove实现
public final boolean remove(Object key) {
return removeNode(hash(key), key, null, false, true) != null;
}
(3)HashMap.EntrySet的remove实现
public final boolean remove(Object o) {
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>) o;
Object key = e.getKey();
Object value = e.getValue();
return removeNode(hash(key), key, value, true, true) != null;
}
return false;
}
(4)HashMap.HashIterator的remove方法实现
public final void remove() {
Node<K,V> p = current;
if (p == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
current = null;
K key = p.key;
removeNode(hash(key), key, null, false, false);
expectedModCount = modCount; //----------------这里将expectedModCount 与modCount进行同步}
以上几种方式都通过调用HashMap.removeNode方法来实现删除key的操作。在removeNode方法内只要移除了key, modCount就会执行一次自增操作,此时modCount就与expectedModCount不一致了;
上面几种remove实现中,只有第三种iterator的remove方法在调用完removeNode方法后同步了expectedModCount值与modCount相同,所以在遍历下个元素调用nextNode方法时,iterator方式不会抛异常。
到这里是不是有一种恍然大明白的感觉呢!
为了解决这个问题,有一些可行的方式: