1

假设一个映射包含整数键和一个字符串列表作为它的值。然后,我不能这样做:

for (Map.Entry<Integer, List<String>> entry : map.entrySet()){
    for (String string : entry.getValue()){
        if (string.startsWith("a")){
           entry.getValue().remove(string);
        }
    }
}

它抛出ConcurrentModificationException。但是,如果我执行以下操作:

for (Map.Entry<Integer, List<String>> entry : map.entrySet()){
    entry.setValue(new ArrayList<String>());
}

这完美地工作。我们现在不是在修改底层地图吗?

4

2 回答 2

7

问题与 无关Map,仅与您使用值列表的方式有关。以下将失败,任何ArrayList

for (String string : list){
    if (string.startsWith("a")){
       list.remove(string);
    }
}

其原因在以下的 Javadoc 中进行了讨论ArrayList

此类的 iterator 和 listIterator 方法返回的迭代器是快速失败的:如果在创建迭代器后的任何时间对列表进行结构修改,除了通过迭代器自己的 remove 或 add 方法之外的任何方式,迭代器将抛出 ConcurrentModificationException。因此,面对并发修改,迭代器快速而干净地失败,而不是在未来不确定的时间冒任意的、非确定性的行为。

(换句话说:如果从列表中删除元素,迭代器可能不再指向底层数组中的正确索引。因此,它不允许您使用可能损坏的迭代器,而是出于ConcurrentModificationException礼貌而抛出 a让你知道你需要重新设计你的程序。)

一个简单的解决方法是使用

Iterator<String> itr = entry.getValue().iterator();
while (itr.hasNext()) {
  if (itr.next().startsWith("a")) {
    itr.remove();
  }
}
于 2012-08-13T16:48:48.300 回答
3

请查看 HashMap.java 中 entrySet() 的 Javadoc,您会发现原因!

文档中

返回此映射中包含的映射的 Set 视图。集合由地图支持,因此对地图的更改会反映在集合中,反之亦然。如果在对集合进行迭代时修改了映射(通过迭代器自己的删除操作或通过迭代器返回的映射条目上的 setValue 操作除外),则迭代的结果是未定义的。该集合支持元素移除,即通过 Iterator.remove、Set.remove、removeAll、retainAll 和 clear 操作从映射中移除相应的映射。它不支持 add 或 addAll 操作。

指定者:Map中的entrySet(),

覆盖:AbstractMap 中的 entrySet()

返回:此映射中包含的映射的集合视图

于 2012-08-13T16:43:20.387 回答