我有一段简单的代码,它循环遍历映射,检查每个条目的条件,如果条件为真,则在条目上执行一个方法。之后该条目将从地图中删除。 要从地图中删除条目,我使用
Iterator
来避免 ConcurrentModificationException
。
除了我的代码确实抛出异常,在
it.remove()
行:
Caused by: java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.remove(Unknown Source) ~[?:1.8.0_161]
at package.Class.method(Class.java:34) ~[Class.class:?]
经过长时间的搜索,我找不到解决此问题的方法,所有答案都建议使用
Iterator.remove()
方法,但我已经在使用它了。 Map.entrySet()
的文档明确指出可以使用 Iterator.remove()
方法从集合中删除元素。
任何帮助将不胜感激。
Iterator<Entry<K, V>> it = map.entrySet().iterator();
while (it.hasNext()) {
Entry<K, V> en = it.next();
if (en.getValue().shouldRun()) {
EventQueue.invokeLater(()->updateSomeGui(en.getKey())); //the map is in no way modified in this method
en.getValue().run();
it.remove(); //line 34
}
}
如果您无法将
HashMap
更改为 ConcurrentHashMap
,您可以对代码使用另一种方法。
您可以创建一个包含要删除的条目的条目列表,然后迭代它们并将它们从原始映射中删除。
例如
HashMap<String, String> map = new HashMap<>();
map.put("1", "a1");
map.put("2", "a2");
map.put("3", "a3");
map.put("4", "a4");
map.put("5", "a5");
Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
List<Map.Entry<String, String>> entries = new ArrayList<>();
while (iterator.hasNext()) {
Map.Entry<String, String> next = iterator.next();
if (next.getKey().equals("2")) {
/* instead of remove
iterator.remove();
*/
entries.add(next);
}
}
for (Map.Entry<String, String> entry: entries) {
map.remove(entry.getKey());
}
当您在多个线程中操作对象时,请使用 ConcurrentHashMap 代替 HashMap。 HashMap 类不是线程安全的,也不允许此类操作。请参阅下面的链接以获取与此相关的更多信息。
https://www.google.co.in/amp/s/www.geeksforgeeks.org/difference-hashmap-concurrenthashmap/amp/
请告诉我更多信息。
因为我已经在这里并且没有“完整”的答案,所以它是:
HashMap 类文档明确指出,如果多个线程访问
HashMap
实例并且至少一个在结构上修改了它,则必须在外部同步HashMap实例(通常使用Collections.synchronizedMap
)。
请注意,此实现不是同步的。如果多个线程同时访问哈希映射,并且至少有一个线程在结构上修改映射,则它必须在外部同步。 (结构修改是添加或删除一个或多个映射的任何操作;仅更改与实例已包含的键关联的值不是结构修改。)这通常是通过在自然封装映射的某个对象上进行同步来完成的。如果不存在这样的对象,则应使用 Collections.synchronizedMap 方法“包装”地图。
它还说更改与键关联的值不是结构修改,但没有说明这些情况是否总是产生定义的行为(可能会发生竞争条件,IDK)。
现在是
ConcurrentModificationException
。当它可以知道在支持 Map
中执行了结构修改而不是通过这个特定的迭代器 remove()
方法进行时,它由 fail-fast 迭代器引发。也就是说,当您在迭代器处于活动状态时更改支持
Map
(如果未使用迭代器 remove()
执行此修改)时,单线程程序中的迭代器也可能引发此异常。
来自 ConcurrentModificationException 文档:
请注意,此异常并不总是表明某个对象已被“不同”线程同时修改。如果单个线程发出一系列违反对象约定的方法调用,则该对象可能会抛出此异常。例如,如果一个线程在使用快速失败迭代器迭代集合时直接修改集合,则迭代器将抛出此异常。
在您的情况下,如果程序中的一个线程从
HashMap
实例实例化一个迭代器只是为了读取,并且另一个线程也从同一个
Map
实例实例化一个迭代器,并且这些迭代器的生命周期重叠,如果一个迭代器调用remove()
,那么其他人就可以举ConcurrentModificationException
。keySet() 允许您迭代键。这对你没有帮助,因为钥匙是 通常是不可变的。
如果您只想访问地图值,则需要使用示例:将所有包含大写的键的值转换为大写values() 。 如果它们是可变对象,则可以直接更改,无需放置 他们回到地图中。
entrySet() 最强大的版本,可让您更改条目的值 直接。
for(Map.Entry<String, String> entry:map.entrySet()){
if(entry.getKey().contains("_"))
entry.setValue(entry.getValue().toUpperCase());
}
实际上,如果您只想编辑值对象,请使用值集合来完成。我假设你的地图属于:
类型
for(Object o: map.values()){
if(o instanceof MyBean){
((Mybean)o).doStuff();
}
}