返回

深入剖析 HashMap 删除操作中的 ConcurrentModificationException

后端

深入理解 HashMap 中的 ConcurrentModificationException

简介

HashMap 在 Java 中以其卓越的查找和插入性能而闻名。然而,当我们在遍历 HashMap 的同时尝试删除元素时,可能会遭遇一个棘手的异常:ConcurrentModificationException。这篇文章将深入剖析导致此异常的原因,并提供应对技巧,助你轻松解决。

ConcurrentModificationException 揭秘

ConcurrentModificationException 是当集合在结构上发生变化(例如添加或删除元素)时,另一个线程同时尝试遍历它时引发的。具体到 HashMap,当你在遍历键值对时,另一个线程试图删除或修改同一 HashMap,就会出现这种情况。

本质上,HashMap 使用迭代器来遍历其键值对。当你创建迭代器时,它会指向 HashMap 的底层结构。但如果你在遍历过程中对 HashMap 进行修改(例如使用 remove() 方法),底层结构就会发生变化,而迭代器却依然指向旧的结构。

这种错位导致了 ConcurrentModificationException,因为迭代器试图遍历一个不再存在的结构。

避免 ConcurrentModificationException 的妙招

避免 ConcurrentModificationException 的关键是确保在遍历 HashMap 时不对其进行修改。有几个有效的方法:

1. 并发版本:ConcurrentHashMap

ConcurrentHashMap 是 HashMap 的并发版本,它使用不同的锁机制来防止 ConcurrentModificationException。如果你需要在多线程环境中遍历 HashMap,使用 ConcurrentHashMap 是一个明智的选择。

2. CopyOnWrite 容器

CopyOnWriteArrayList 和 CopyOnWriteArraySet 等 CopyOnWrite 容器在每次对其进行修改时都会创建一个 HashMap 的副本。这可以防止对原始 HashMap 进行并发修改,从而避免 ConcurrentModificationException。

3. 同步:synchronized 块

你可以在遍历 HashMap 时使用 synchronized 块来同步对它的访问。这将确保在任何时刻只有一个线程可以遍历 HashMap,从而防止 ConcurrentModificationException。

代码示例

使用 ConcurrentHashMap 避免 ConcurrentModificationException:

import java.util.concurrent.ConcurrentHashMap;

public class HashMapWithConcurrentModificationException {

    public static void main(String[] args) {
        // 使用 ConcurrentHashMap 避免 ConcurrentModificationException
        ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

        map.put("key1", 1);
        map.put("key2", 2);
        map.put("key3", 3);

        for (String key : map.keySet()) {
            // 安全地从 ConcurrentHashMap 中删除元素
            map.remove(key);
        }
    }
}

使用 CopyOnWriteArrayList 避免 ConcurrentModificationException:

import java.util.concurrent.CopyOnWriteArrayList;

public class HashMapWithConcurrentModificationException {

    public static void main(String[] args) {
        // 使用 CopyOnWriteArrayList 避免 ConcurrentModificationException
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();

        list.add("key1");
        list.add("key2");
        list.add("key3");

        for (String key : list) {
            // 安全地从 CopyOnWriteArrayList 中删除元素
            list.remove(key);
        }
    }
}

常见问题解答

1. 为什么会出现 ConcurrentModificationException?

当另一个线程在遍历 HashMap 时对其进行结构修改时,就会出现 ConcurrentModificationException。

2. 如何避免 ConcurrentModificationException?

你可以使用 ConcurrentHashMap、CopyOnWrite 容器或同步块来避免 ConcurrentModificationException。

3. ConcurrentHashMap 和 CopyOnWrite 容器有什么区别?

ConcurrentHashMap 使用锁机制,而 CopyOnWrite 容器在每次修改时都会创建一个副本。

4. 同步块如何防止 ConcurrentModificationException?

同步块确保在任何时刻只有一个线程可以遍历 HashMap。

5. 为什么在遍历时删除元素会导致 ConcurrentModificationException?

因为底层结构会发生变化,而迭代器依然指向旧的结构,导致错位。

总结

了解 ConcurrentModificationException 的原因并掌握避免它的技巧,是使用 HashMap 的关键。通过使用 ConcurrentHashMap、CopyOnWrite 容器或同步块,你可以防止并发修改,确保流畅无误的遍历。