[英]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.