开发数据库函数依赖算法时遭遇ArrayList与HashMap的ConcurrentModificationException
嘿,我之前在处理集合遍历+修改的场景时也踩过一模一样的坑,咱们来拆解一下问题,然后给出靠谱的解决方案!
为什么会出现这个异常?
你遇到的ConcurrentModificationException,本质上是因为你在使用迭代器遍历集合(比如ArrayList的增强for循环)的同时,直接调用了集合本身的remove()方法修改它。迭代器会维护一个修改计数,当它发现集合的实际修改次数和自己记录的不一致时,就会抛出这个异常——哪怕你是在单线程下操作,只要遍历和修改的方式不对,就会触发。
在你的场景里,就是遍历Map中List<String>类型的值时,直接调用了List.remove(),触发了迭代器的并发修改检测。
几种可行的解决方案
1. 使用迭代器的remove()方法(最标准的做法)
迭代器本身提供了安全的remove()方法,它会同步更新迭代器的修改计数,不会触发异常。把你的增强for循环改成用迭代器遍历:
// 假设你的Map结构是这样的 Map<String, List<String>> dependencyMap = ...; List<String> headerList = ...; for (String header : headerList) { // 遍历每个字符串的字符 for (char c : header.toCharArray()) { String targetChar = String.valueOf(c); // 假设你要操作的是Map中某个key对应的List List<String> values = dependencyMap.get("你的目标Key"); if (values == null) continue; // 使用迭代器遍历并安全移除 Iterator<String> iter = values.iterator(); while (iter.hasNext()) { String val = iter.next(); // 这里替换成你的移除判断逻辑 if (val.contains(targetChar)) { iter.remove(); // 用迭代器的remove方法,不会抛异常 } } } }
2. 使用Java 8+的removeIf()方法(更简洁)
如果你的移除逻辑可以用一个谓词表达式描述,List.removeIf()是更优雅的选择——它内部已经处理了安全遍历和修改的逻辑:
for (String header : headerList) { for (char c : header.toCharArray()) { String targetChar = String.valueOf(c); List<String> values = dependencyMap.get("你的目标Key"); if (values == null) continue; // 一行搞定安全移除 values.removeIf(val -> val.contains(targetChar)); } }
3. 遍历集合的副本,修改原集合
如果上面两种方式不适合你的场景,还可以先把原集合复制一份,遍历这个副本,然后修改原集合。这样原集合的修改不会影响遍历过程:
for (String header : headerList) { for (char c : header.toCharArray()) { String targetChar = String.valueOf(c); List<String> values = dependencyMap.get("你的目标Key"); if (values == null) continue; // 创建原集合的副本 List<String> valuesCopy = new ArrayList<>(values); for (String val : valuesCopy) { if (val.contains(targetChar)) { values.remove(val); // 遍历副本,修改原集合 } } } }
额外提醒
如果你的Map本身也在被遍历的同时修改(比如遍历Map的key时添加/删除key),那还要注意Map的安全遍历——比如用entrySet()的迭代器,或者使用ConcurrentHashMap(但要注意,ConcurrentHashMap只是保证Map本身的线程安全,它的value如果是ArrayList的话,还是要按照上面的方法处理List的遍历修改)。
内容的提问来源于stack exchange,提问作者Aymen Algawi

