当前位置: 首页 > news >正文

Java 开发 - HashMap 遍历元素的同时删除元素抛出 ConcurrentModificationException 异常(原理分析、解决方案)

一、问题引入

Map<String, String> map = new HashMap<>();map.put("test1", "this is test1");
map.put("demo2", "this is demo2");
map.put("test3", "this is test3");
map.put("test4", "this is test4");map.forEach((key, value) -> {System.out.println(key + " - " + value);
});System.out.println("------------------------------");map.forEach((key, value) -> {if (key.startsWith("test")) {map.remove(key);}
});map.forEach((key, value) -> {System.out.println(key + " - " + value);
});
# 输出结果test4 - this is test4
test3 - this is test3
test1 - this is test1
demo2 - this is demo2
------------------------------
Exception in thread "main" java.util.ConcurrentModificationException
  • 在 Java 开发中,使用 HashMap 遍历元素的同时删除元素,会抛出 ConcurrentModificationException 异常

二、原理分析

  1. HashMap 内部有一个 modCount,用于记录结构修改次数
public class HashMap<K,V> extends AbstractMap<K,V>implements Map<K,V>, Cloneable, Serializable {...transient int modCount;...
}
  1. 当执行 put、remove、clear 操作时,modCount 会增加
public V put(K key, V value) {return putVal(hash(key), key, value, false, true);
}final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {Node<K,V>[] tab; Node<K,V> p; int n, i;if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length;if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);else {Node<K,V> e; K k;if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))e = p;else if (p instanceof TreeNode)e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);else {for (int binCount = 0; ; ++binCount) {if ((e = p.next) == null) {p.next = newNode(hash, key, value, null);if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1sttreeifyBin(tab, hash);break;}if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))break;p = e;}}if (e != null) { // existing mapping for keyV oldValue = e.value;if (!onlyIfAbsent || oldValue == null)e.value = value;afterNodeAccess(e);return oldValue;}}++modCount;if (++size > threshold)resize();afterNodeInsertion(evict);return null;
}
public V remove(Object key) {Node<K,V> e;return (e = removeNode(hash(key), key, null, false, true)) == null ?null : e.value;
}final Node<K,V> removeNode(int hash, Object key, Object value,boolean matchValue, boolean movable) {Node<K,V>[] tab; Node<K,V> p; int n, index;if ((tab = table) != null && (n = tab.length) > 0 &&(p = tab[index = (n - 1) & hash]) != null) {Node<K,V> node = null, e; K k; V v;if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))node = p;else if ((e = p.next) != null) {if (p instanceof TreeNode)node = ((TreeNode<K,V>)p).getTreeNode(hash, key);else {do {if (e.hash == hash &&((k = e.key) == key ||(key != null && key.equals(k)))) {node = e;break;}p = e;} while ((e = e.next) != null);}}if (node != null && (!matchValue || (v = node.value) == value ||(value != null && value.equals(v)))) {if (node instanceof TreeNode)((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);else if (node == p)tab[index] = node.next;elsep.next = node.next;++modCount;--size;afterNodeRemoval(node);return node;}}return null;
}
public void clear() {Node<K,V>[] tab;modCount++;if ((tab = table) != null && size > 0) {size = 0;for (int i = 0; i < tab.length; ++i)tab[i] = null;}
}
  1. 当创建迭代器时,会记录当前的 modCount 作为 expectedModCount

  2. 在迭代过程中,每次调用 next 方法时都会检查

abstract class HashIterator {Node<K,V> next;        // next entry to returnNode<K,V> current;     // current entryint expectedModCount;  // for fast-failint index;             // current slotHashIterator() {expectedModCount = modCount;Node<K,V>[] t = table;current = next = null;index = 0;if (t != null && size > 0) { // advance to first entrydo {} while (index < t.length && (next = t[index++]) == null);}}public final boolean hasNext() {return next != null;}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;}public final void remove() {Node<K,V> p = current;if (p == null)throw new IllegalStateException();if (modCount != expectedModCount)throw new ConcurrentModificationException();current = null;removeNode(p.hash, p.key, null, false, false);expectedModCount = modCount;}
}
final class KeyIterator extends HashIteratorimplements Iterator<K> {public final K next() { return nextNode().key; }
}final class ValueIterator extends HashIteratorimplements Iterator<V> {public final V next() { return nextNode().value; }
}final class EntryIterator extends HashIteratorimplements Iterator<Map.Entry<K,V>> {public final Map.Entry<K,V> next() { return nextNode(); }
}

三、异常流程分析

  1. 创建 HashMap,添加 4 个元素,此时,modCount 为 4

  2. 开始遍历,forEach 内部创建迭代器,执行 expectedModCount = modCount;,此时,modCount 为 4,expectedModCount 为 4

  3. 处理元素,处理到 test1 时,满足条件,调用 remove 方法,此时,modCount 为 5,expectedModCount 为 4

  4. 继续处理下一个元素,迭代器调用 next 方法,检查 modCount != expectedModCount,抛出 ConcurrentModificationException 异常


四、解决方案

  1. 使用 removeIf 方法
Map<String, String> map = new HashMap<>();map.put("test1", "this is test1");
map.put("demo2", "this is demo2");
map.put("test3", "this is test3");
map.put("test4", "this is test4");map.forEach((key, value) -> {System.out.println(key + " - " + value);
});System.out.println("------------------------------");map.entrySet().removeIf(entry -> entry.getKey().startsWith("test"));map.forEach((key, value) -> {System.out.println(key + " - " + value);
});
  1. 使用迭代器的 remove 方法
Map<String, String> map = new HashMap<>();map.put("test1", "this is test1");
map.put("demo2", "this is demo2");
map.put("test3", "this is test3");
map.put("test4", "this is test4");map.forEach((key, value) -> {System.out.println(key + " - " + value);
});System.out.println("------------------------------");Iterator<String> iterator = map.keySet().iterator();
while (iterator.hasNext()) {String key = iterator.next();if (key.startsWith("test")) {iterator.remove();}
}map.forEach((key, value) -> {System.out.println(key + " - " + value);
});
  1. 收集要删除的元素,然后批量删除
Map<String, String> map = new HashMap<>();map.put("test1", "this is test1");
map.put("demo2", "this is demo2");
map.put("test3", "this is test3");
map.put("test4", "this is test4");map.forEach((key, value) -> {System.out.println(key + " - " + value);
});System.out.println("------------------------------");List<String> toRemove = map.keySet().stream().filter(key -> key.startsWith("test")).toList();toRemove.forEach(key -> map.remove(key));map.forEach((key, value) -> {System.out.println(key + " - " + value);
});

五、补充学习

  • ConcurrentHashMap 可以使用 forEach 遍历元素的同时删除元素
Map<String, String> map = new ConcurrentHashMap<>();map.put("test1", "this is test1");
map.put("demo2", "this is demo2");
map.put("test3", "this is test3");
map.put("test4", "this is test4");map.forEach((key, value) -> {System.out.println(key + " - " + value);
});System.out.println("------------------------------");map.forEach((key, value) -> {if (key.startsWith("test")) {map.remove(key);}
});map.forEach((key, value) -> {System.out.println(key + " - " + value);
});
# 输出结果test4 - this is test4
test3 - this is test3
test1 - this is test1
demo2 - this is demo2
------------------------------
demo2 - this is demo2
http://www.dtcms.com/a/582571.html

相关文章:

  • 昆明如何做百度的网站做足彩推荐赚钱的网站
  • WSL2 安装Ubuntu卡在安装进度0%无响应问题解决
  • 微服务网站天津网站制作网页
  • h5互动网站建设顺企网官网
  • Maven介绍
  • C++ 递推与递归:两种算法思想的深度解析与实战
  • WPP Media(群邑)优质服务能力 再度蝉联京东京牌五星认证
  • 装修公司网站建设费用宁波seo基础入门
  • mysql 网站 数据库站长之家工具高清
  • 如何在k8s中配置并使用nvidia显卡
  • 【天野学院5期】 第5期易语言半内存辅助培训班,主讲游戏——手游:仙剑奇侠传4,端游:神魔大陆2
  • 深度学习-损失函数
  • 淮南建设公司网站如何做外围网站的代理
  • 测试开发话题11---自动化测试实战篇
  • 单播、广播、组播
  • 不用建网站怎么做淘宝客wordpress 分类分页
  • 防水网站的外链如何找临汾市网站建设
  • 公司app与网站建设方案网站域名备案密码
  • 【智慧城市】2025年华中农业大学暑期实训优秀作品(2):基于Vue框架和Java后端开发
  • C++面试常见问题
  • 品牌网站建设权威logo库官网
  • AI驱动开发新范式:基于 CodeWave 的考勤系统落地实践
  • PCI总线驱动开发全解析
  • 做网站数据库表设计Wordpress企业主题XShuan
  • 买完域名网站怎么设计房产中介网站开发模板
  • AVL树实现
  • Vue 组件插槽的深层传递
  • HENGSHI SENSE 6.1 发布,从 ChatBI 到 Agentic Analytics
  • 网站 哪些服务器wordpress新编辑器分类
  • 网站进度条源代码juqery-ui泰安网站建设公司