我有以下错误:
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
,keySet
或values
方法返回的集合在迭代时不允许更改(插入,删除)。您可以创建一个临时列表来循环,同时仍然更改原始映射:
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。