在hashmap上循环时得到concurrentmodification错误



我有以下错误:

Exception in thread "Thread-0" java.util.ConcurrentModificationException
at java.base/java.util.HashMap$HashIterator.nextNode(HashMap.java:1584)
at java.base/java.util.HashMap$KeyIterator.next(HashMap.java:1607)
at Server$1.run(Server.java:149)
at java.base/java.lang.Thread.run(Thread.java:832)

指这段代码:

for (Session key : sessions.keySet()) {
if (key.getPort2() != port && key.getPort1() != port) { // change later to ip
System.out.println("2nd time init 2 client");
session.setIp2(ip);
session.setPort2(port);
sessionID++;
sessions.put(session, sessionID);
// reset session
session = null;
}
}

你能解释一下为什么会这样吗?

HashMap及其entrySey,keySetvalues方法返回的集合在迭代时不允许更改(插入,删除)。您可以创建一个临时列表来循环,同时仍然更改原始映射:

for (Session key : new ArrayList<>(sessions.keySet())) {
...
sessions.put(session, sessionID);
...
}

还可以创建一个临时列表来存储要插入的元素(在循环之后)。


不是问题,但不确定这部分是用来做什么的:

// reset session
session = null;

kind ofdangerous->NullPointerException的后续迭代(假设if块被执行)

在迭代地图内容的同时修改地图,这是不允许的。

几乎所有的集合类都有很好的理由禁止这样做。假设您有一个包含

的集合
a b c g h i j k l

现在你遍历元素,在处理元素k的时候插入d。你会期望d被跳过,因为你已经超越了那个位置吗?如果你在位置b插入一个m呢?你认为m会被处理吗?或者您是否期望新元素不会被处理,因为它们在您开始迭代时不存在?

如果Java允许这样做,人们就会抱怨意想不到的行为。这就是Java不允许这样做的原因。与旧的编程语言相比,避免意外行为是Java的一个主要目标。

要解决这个问题,在开始迭代之前创建第二个临时空映射。然后在循环中,您可以将新元素放入临时映射中。最后将它们合并:

Map<String, String> tmp=new HashMap<>();
for (Session key : sessions.keySet()) {
...
tmp.put(session, sessionID);
}
sessions.putAll(tmp);

这样就清楚发生了什么,没有意外的行为。

java.util。HashMap不是线程安全的,如果你正在遍历HashMap元素,同时如果你试图修改它,JVM将通过ConcurrentModificationException。你可以使用Iterator迭代HashMap元素,也可以使用ConcurrentHashMap。

最新更新