java:为什么在我们迭代它时修改map的条目值是否安全?

Dau*_*aud 1 java concurrency loops map

假设一个映射包含整数键,并且一个字符串列表作为其值.然后,我不能这样做:

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

它抛出ConcurrentModificationException.但如果我做以下事情:

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

这非常有效.我们现在不是在修改底层地图吗?

Lou*_*man 7

问题与Map您使用值列表的方式无关.以下将失败ArrayList:

for (String string : list){
    if (string.startsWith("a")){
       list.remove(string);
    }
}
Run Code Online (Sandbox Code Playgroud)

其原因在Javadoc中讨论ArrayList:

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

(换一种方式:如果你从列表中删除元素,可能不是指向底层数组中的右手食指再迭代因此,而不是让你使用可能损坏的迭代器,它抛出一个ConcurrentModificationException出于礼貌让你知道你需要重新设计你的程序.)

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

Iterator<String> itr = entry.getValue().iterator();
while (itr.hasNext()) {
  if (itr.next().startsWith("a")) {
    itr.remove();
  }
}
Run Code Online (Sandbox Code Playgroud)