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

时间:2016-05-11 15:09:48

标签: java collections

我有一张地图

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

我在这里没有例外(我试图修改现有的键值“Manish”)。但是,如果我尝试添加新密钥map.put("Manish123", "Updated"),我会ConcurrentModificationException

3 个答案:

答案 0 :(得分:4)

因为您没有修改iterator

在这种情况下,

put会改变现有条目,因为Map.Entry中已存在具有相同密钥的Map

答案 1 :(得分:0)

如果你看到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).
 */

因此,该字段保留对地图进行结构修改的次数。当预期的修改计数ConcurrentModificationException(在构造此迭代器时初始化为expectedModCount时,此类中的各种迭代器抛出modCount(可能已选择更好的名称),例如,在行Iterator<Map.Entry<String, String>> itr = map.entrySet().iterator();)与modCount不匹配,put在对地图进行结构修改时发生变异(例如,使用新条目调用Manish等)。请注意,此处不涉及不同的线程。所有这些都可以在一个线程中发生,例如,当您从地图中删除条目或在迭代时向其添加条目时)。

正如您现在可以理解的那样,将现有密钥重新映射到不同的值不应导致更改哈希映射的内部结构(因为它只是替换与密钥关联的值)。而你所做的只是简单地将键Updated重复映射到值ConcurrentModificationException,重复次数与映射中的条目一样多次(这是4并且在迭代期间是固定的)。但是,如果添加或删除了任何密钥,您将获得 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);

这类似于以下代码(注意:仅用于说明目的):

[Larry 0, Moe 1, Curly 2]

打印:

{{1}}

答案 2 :(得分:-1)

根据Java API:如果在Iteration启动后修改了Collection,则使用Iterator迭代收集会受ConcurrentModificationException影响,但只有在故障快速迭代器的情况下才会发生这种情况。

Java中有两种类型的迭代器,故障快速和故障安全,检查故障安全和故障快速迭代器之间的区别以获取更多详细信息。

Java中的失败快速迭代器

javaAs名称中的故障安全与故障快速迭代器之间的区别表明,一旦他们意识到自迭代开始以来已经更改了Collection的结构,则失败快速迭代器会失败。结构更改意味着在一个线程迭代该集合时添加,删除或更新集合中的任何元素。通过保持修改计数来实现失败快速行为,如果迭代线程实现修改计数的更改,则会抛出ConcurrentModificationException。

Java doc说这不是一种保证行为,而是完成了最好的努力基础&#34;,因此应用程序编程不能依赖于这种行为。此外,由于在更新和检查修改计数时涉及多个线程,并且此检查在没有同步的情况下完成,因此迭代线程仍有可能仍然看到过时值并且可能无法检测到并行线程所做的任何更改。大多数JDK1.4集合返回的迭代器都是快速失败的,包括Vector,ArrayList,HashSet等

java中的故障安全迭代器

与故障快速迭代器相反,如果在结构上修改了Collection,则故障安全迭代器不会抛出任何异常 虽然有一个线程迭代它,因为它们可以克隆Collection而不是原始集合,这就是为什么它们被称为故障安全迭代器。 CopyOnWriteArrayList的迭代器是一个故障安全Iterator的示例,也是由ConcurrentHashMap编写的迭代器,keySet也是故障安全迭代器,永远不会在Java中抛出ConcurrentModificationException。