简体   繁体   English

为什么我在同时迭代和修改HashMap时没有得到“ConcurrentModificationException”?

[英]How come I am not getting “ConcurrentModificationException” while iterating and modifying on HashMap at the same time?

I have a map 我有一张地图

Map<String, String> map = new HashMap<String, String>();

map.put("Pujan", "pujan");
map.put("Swati", "swati");
map.put("Manish", "manish");
map.put("Jayant", "jayant");
Iterator<Map.Entry<String, String>> itr = map.entrySet().iterator();
while(itr.hasNext()){
  Entry<String,String> entry=(Entry<String, String>) itr.next();
  map.put("Manish", "Updated");
}

I don't get an exception here (where I am trying to modify an existing key value "Manish"). 我在这里没有例外(我试图修改现有的键值“Manish”)。 But if I try to add a new key map.put("Manish123", "Updated") I get ConcurrentModificationException . 但是如果我尝试添加一个新的键map.put("Manish123", "Updated")我会得到ConcurrentModificationException

Because your aren't modifying the iterator , 因为你没有修改iterator

put will mutate an existing entry in this case because a Map.Entry with the same key already exists in the Map . 在这种情况下, put会改变现有条目,因为Map已经存在具有相同键的Map.Entry

If you see the Javadoc for the modCount field of a HashMap (in Java 8 HashMap.java source), you will see: 如果您看到HashMap的modCount字段的Javadoc(在Java 8 HashMap.java源代码中),您将看到:

/**
 * The number of times this HashMap has been structurally modified.
 * Structural modifications are those that change the number of mappings in
 * the HashMap or otherwise modify its internal structure (e.g.,
 * rehash).  This field is used to make iterators on Collection-views of
 * the HashMap fail-fast.  (See ConcurrentModificationException).
 */

Thus this field keeps the number of times there have been structural modifications to the map. 因此,该字段保持对地图进行结构修改的次数。 The various iterators in this class throw the ConcurrentModificationException (a better name could have been chosen) when the expected modification count expectedModCount (which is initialized to modCount when you construct this iterator, for example, at the line Iterator<Map.Entry<String, String>> itr = map.entrySet().iterator(); ) does not match modCount which is mutated any time there are structural modifications to the map (eg calling put with a new entry, among other things). 当预期的修改计数expectedModCount (在构造此迭代器时初始化为modCount ,例如,在行Iterator<Map.Entry<String, String>> itr = map.entrySet().iterator();此类中的各种迭代器抛出ConcurrentModificationException (可能已经选择了更好的名称)) Iterator<Map.Entry<String, String>> itr = map.entrySet().iterator(); )与modCount不匹配,只要对地图进行结构修改就会发生变异(例如,使用新条目调用put等)。 Note that different threads are not involved here. 请注意,此处不涉及不同的线程。 All of this can happen in one single thread, for example, when you remove entries from the map or add entries to it while iterating). 所有这些都可以在一个线程中发生,例如,当您从地图中删除条目或在迭代时向其添加条目时)。

As you can now relate, remapping an existing key to a different value should not result in changes to the internal structure of the hash map (since it simply replaces the value associated with the key). 正如您现在可以理解的那样,将现有密钥重新映射到不同的值不应导致更改哈希映射的内部结构(因为它只是替换与密钥关联的值)。 And all you are doing is simply remapping the key Manish to a value Updated repeatedly as many times as there are entries in the map (which is 4 and is fixed for the duration of iteration). 而你所做的只是将关键Manish重新映射到一个值,重复Updated次数与地图中的条目一样多次(这是4并且在迭代期间是固定的)。 If, however, added or removed any key you will get the ConcurrentModificationException . 但是,如果添加或删除任何键,您将获得ConcurrentModificationException

This is analogous to the following code (Note: for illustration purposes only): 这类似于以下代码(注意:仅用于说明目的):

    List<String> names = Arrays.asList("Larry", "Moe", "Curly");
    int i = 0;
    Iterator<String> strIter = names.iterator();
    while (strIter.hasNext()) {
        names.set(i, strIter.next() + " " + i); // value changed, no structural modification to the list
        i += 1;
    }
    System.out.println(names);

which prints: 打印:

[Larry 0, Moe 1, Curly 2]

According to Java API : Iterating over collection using Iterator is subject to ConcurrentModificationException if Collection is modified after Iteration started, but this only happens in case of fail-fast Iterators. 根据Java API:如果在迭代开始后修改了Collection,则使用Iterator对集合进行迭代将受ConcurrentModificationException限制,但这只发生在故障快速迭代器中。

There are two types of Iterators in Java, fail-fast and fail-safe, check difference between fail-safe and fail-fast Iterator for more details. Java中有两种类型的迭代器,故障快速和故障安全,检查故障安全和故障快速迭代器之间的差异以获取更多详细信息。

Fail-Fast Iterators in Java Java中的失败快速迭代器

Difference between fail-safe vs fail-fast iterator in javaAs name suggest fail-fast Iterators fail as soon as they realized that structure of Collection has been changed since iteration has begun. javaAs名称中的故障安全与故障快速迭代器之间的区别表明,一旦他们意识到自迭代开始以来已经更改了Collection的结构,则失败快速迭代器会失败。 Structural changes means adding, removing or updating any element from collection while one thread is Iterating over that collection. 结构更改意味着在一个线程迭代该集合时添加,删除或更新集合中的任何元素。 fail-fast behavior is implemented by keeping a modification count and if iteration thread realizes the change in modification count it throws ConcurrentModificationException. 通过保持修改计数来实现失败快速行为,并且如果迭代线程实现修改计数的更改,则它会抛出ConcurrentModificationException。

Java doc says this is not a guaranteed behavior instead its done of "best effort basis", So application programming can not rely on this behavior. Java doc说这不是一个保证的行为,而是完成了“尽力而为”的基础,因此应用程序编程不能依赖于这种行为。 Also since multiple threads are involved while updating and checking modification count and this check is done without synchronization, there is a chance that Iteration thread still sees a stale value and might not be able to detect any change done by parallel threads. 此外,由于在更新和检查修改计数时涉及多个线程,并且此检查在没有同步的情况下完成,因此迭代线程仍有可能仍然看到过时值并且可能无法检测到并行线程所做的任何更改。 Iterators returned by most of JDK1.4 collection are fail-fast including Vector, ArrayList, HashSet etc 大多数JDK1.4集合返回的迭代器都是快速失败的,包括Vector,ArrayList,HashSet等

Fail-Safe Iterator in java java中的故障安全迭代器

Contrary to fail-fast Iterator, fail-safe iterator doesn't throw any Exception if Collection is modified structurally while one thread is Iterating over it because they work on clone of Collection instead of original collection and that's why they are called as fail-safe iterator. 与失败快速的Iterator相反,如果Collection在结构上被修改而一个线程正在迭代它,因为它们用于复制Collection而不是原始集合,那么故障安全迭代器不会抛出任何Exception,这就是为什么它们被称为故障保护的原因迭代器。 Iterator of CopyOnWriteArrayList is an example of fail-safe Iterator also iterator written by ConcurrentHashMap keySet is also fail-safe iterator and never throw ConcurrentModificationException in Java. CopyOnWriteArrayList的迭代器是一个故障安全Iterator的示例,也是由ConcurrentHashMap写的迭代器,keySet也是故障安全迭代器,永远不会在Java中抛出ConcurrentModificationException。

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

相关问题 在线程类中修改HashMap时获取ConcurrentModificationException - Getting ConcurrentModificationException while modifying a HashMap in a thread class 如何在迭代Hashmap时获取ConcurrentModificationException? - How I can get ConcurrentModificationException while iterating Hashmap? 通过HashMap迭代时出现ConcurrentModificationException - ConcurrentModificationException while iterating through HashMap 我在使用HashMap时抛出java.util.ConcurrentModificationException - I am getting java.util.ConcurrentModificationException thrown while using HashMap 知道为什么我在从 HashMap 中删除键时没有收到 java.util.ConcurrentModificationException 吗? - Any Idea why I am not getting java.util.ConcurrentModificationException while removing key from HashMap? 遍历列表但未修改列表时发生ConcurrentModificationException - ConcurrentModificationException while iterating through a list but not modifying it 遍历List时发生ConcurrentModificationException,尽管未对其进行任何修改 - ConcurrentModificationException while iterating through List, altough not modifying it 为什么我会收到 ConcurrentModificationException? - Why am I getting a ConcurrentModificationException? 在遍历数组列表时对其进行修改 - Modifying an Array list while I am iterating over it 如何在避免ConcurrentModificationException的同时迭代HashMap - How to iterate over a HashMap while avoiding ConcurrentModificationException
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM