这里有趣的问题。为什么第一个版本会抛出并发修改错误,而第二个版本则不会。这会发生吗?
Map<String,Integer> map = new HashMap<>();
... // Populate the map
for(String key : map.keySet()){
if(map.get(key) < 50){
map.remove(key);
}
}
Map<String,Integer> map = new HashMap<>();
... // Populate the map
for(String key : new ArrayList<String>(map.keySet())){
if(map.get(key) < 50){
map.remove(key);
}
}
答案 0 :(得分:4)
第一个示例引发异常,因为您在迭代时修改了地图。这是预期的。
在第二个示例中,您将创建一个包含地图中所有字符串的ArrayList。在这里迭代新创建的ArrayList,所以你的第二个例子不会抛出一个例子,因为你遍历ArrayList,而不是遍历地图
答案 1 :(得分:0)
此
for(String key : map.keySet()){
if(map.get(key) < 50){
map.remove(key);
}
}
将始终抛出ConcurrentModificationException
,因为您在迭代时删除了项目。您需要的是具有Iterator
操作的remove
:
for(Iterator<Map.Entry<String, Integer>> it = map.entrySet().iterator(); it.hasNext(); ) {
Map.Entry<String, Integer> entry = it.next();
if(entry.getValue() < 50) {
it.remove();
}
}
还有ConcurrentHashMap
支持并发操作,只在需要时锁定桶。
答案 2 :(得分:0)
在第一种情况下,您会收到concurrentModificationException,因为在内部实现方面存在与迭代相关的修改计数。如果修改计数在迭代期间发生更改,则会引发concurrentModificaitonException。
解决方案是使用iterator.remove()而不是直接从地图中删除元素。
在第二种情况下,在从地图中删除元素时,您不是迭代地图而是迭代不同的集合。在这种情况下,修改计数永远不会在迭代期间更改 ,因为您正在迭代不同的集合。
此外,在多线程环境中,在迭代之前总是在表示类中共享可变状态的集合上使用synchronized,否则可能会遇到concurrentModificationException。
在多线程环境中,您的第二个解决方案不正确,因为您尚未同步将原始地图的键集转移到新集合的语句。因此有可能获得concurrentModificationException。在多线程环境中使用ConcurrentHashMap,同时知道并非默认情况下ConcurrentHashMap上的每个操作或操作集都是线程安全的。