简体   繁体   English

Java并发修改错误HashMap

[英]Java Concurrent Modification Error HashMap

Fun question here. 这里有趣的问题。 Why does the first version of this throw a concurrent modification error, while the second does not. 为什么第一个版本会抛出并发修改错误,而第二个版本则不会。 Should this happen? 这会发生吗?

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);
    }
}

The first example throws an exception because you modify the map while you are iterating over it. 第一个示例引发异常,因为您在迭代时修改了地图。 That is expected. 这是预料之中的。

In the second example, you create an ArrayList containing all the strings in the map. 在第二个示例中,您将创建一个包含地图中所有字符串的ArrayList。 Here you iterate over that newly created ArrayList, so your second example don't throw an example because you iterate over the ArrayList, not over the map 在这里迭代新创建的ArrayList,所以你的第二个例子不会抛出一个例子,因为你遍历ArrayList,而不是遍历地图

This 这个

for(String key : map.keySet()){
    if(map.get(key) < 50){
        map.remove(key);
    }
}

will always throw a ConcurrentModificationException because you remove items while you iterate. 将始终抛出ConcurrentModificationException因为您在迭代时删除项目。 What you need is an Iterator which has the remove operation: 你需要的是一个具有remove操作的Iterator

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();
    }
}

There is also the ConcurrentHashMap which supports concurrent operations and only locks buckets if you need it. 还有ConcurrentHashMap支持并发操作,只在需要时锁定桶。

In the first case you get a concurrentModificationException because there's a modification count associated with iteration in terms of internal implementation. 在第一种情况下,您会得到concurrentModificationException,因为在内部实现方面存在与迭代相关的修改计数。 If the modification count changes during iteration a concurrentModificaitonException is thrown. 如果修改计数在迭代期间发生更改,则会引发concurrentModificaitonException。

The solution is to use iterator.remove() instead of removing an element directly from the map. 解决方案是使用iterator.remove()而不是直接从地图中删除元素。

In the second case, you are iterating not the map but a different collection while removing an element from the map. 在第二种情况下,在从地图中删除元素时,不是迭代地图而是迭代不同的集合。 In this case, the modification count never changes during iteration because you are iterating a different collection. 在这种情况下,修改计数在迭代期间永远不会更改因为您正在迭代不同的集合。

Also, in a multithreaded environment always use synchronized on a collection that represents shared mutable state in a class before iterating it otherwise you can get a concurrentModificationException . 此外,在多线程环境中,在迭代之前始终对表示集合中的共享可变状态的集合使用synchronized,否则可以获得concurrentModificationException。

In a multithreaded environment, your second solution would not be correct because you haven't synchronized the statement where you transfer the keyset of the original map to a new collection. 在多线程环境中,您的第二个解决方案将不正确,因为您尚未同步将原始映射的键集传输到新集合的语句。 So there's potential to get a concurrentModificationException. 因此有可能获得concurrentModificationException。 Use a ConcurrentHashMap in a multithreaded environment while knowing that not every operation or set of operations on a ConcurrentHashMap is threadsafe by default. 在多线程环境中使用ConcurrentHashMap,同时知道默认情况下并非ConcurrentHashMap上的每个操作或操作集都是线程安全的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM