以下代码引发并发修改异常
import java.util.*;
public class SampleTest
{
public static void main(String[] args) {
LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
map.put("value1","a");
map.put("value2","b");
map.put("value3","c");
map.put("value4","d");
map.put("value5","e");
// sublists
List<LinkedHashMap<String, String>> subList = new ArrayList<>();
for(int i = 0; i<5; i++) {
subList.add(map);
}
List<List<LinkedHashMap<String, String>>> mainList = new LinkedList<>();
for(int i = 0; i<3; i++)
{
mainList.add(subList);
}
List<LinkedHashMap<String,String>> temp = mainList.get(mainList.size() - 1);
List<LinkedHashMap<String,String>> temp2 = mainList.get(mainList.size() - 2);
for(LinkedHashMap<String,String> map2 : temp) {
temp2.add(map); // Exception Thrown Here......
}
}
}
但是我通过创建一个新列表并添加地图来修复代码,最后在temp2循环外添加了新列表
example,
List<LinkedHashMap<String,String>> pp = new ArrayList<>();
for(LinkedHashMap<String,String> map2 : temp) {
pp.add(map);
}
temp2.addAll(pp);
我想详细了解为什么在早期代码中并发发生。
谢谢。
答案 0 :(得分:1)
此代码:
List<List<LinkedHashMap<String, String>>> mainList = new LinkedList<>();
for(int i = 0; i<3; i++)
{
mainList.add(subList);
}
正在subList
中添加mainList
三次。当我说“三遍”时,我的意思是代码将同一实例添加了三遍。您可以在mainList
的任何有效索引处修改元素,并且将修改所有其他元素,因为它们是同一实例。参见this question;这可能对这个概念有所帮助。
因此,此代码:
List<LinkedHashMap<String,String>> temp = mainList.get(mainList.size() - 1);
List<LinkedHashMap<String,String>> temp2 = mainList.get(mainList.size() - 2);
从两个不同的索引中获取相同 List
,并将其分配给两个不同的变量。换句话说,temp == temp2
(引用相等)是true
。
然后,您使用List
变量迭代temp
,同时使用List
变量将元素添加到temp2
:
for(LinkedHashMap<String,String> map2 : temp) {
temp2.add(map); // Exception Thrown Here......
}
但是,temp
和temp2
同样是指相同 List
。您的代码基本上在执行以下操作:
List<Object> list = ...; // create new list and fill it
Object someObj = new Object();
for (Object obj : list) { // iterating list
list.add(someObj); // modifying list while it's being iterated
}
最终结果是您在尝试同时修改List
的同时对其进行了修改。 LinkedList
(或实际上Collection
的任何标准实现)都不允许这样做。来自documentation:
此类的
iterator
和listIterator
方法返回的迭代器是 fail-fast :如果在创建迭代器后的任何时间对列表进行结构修改,除了通过迭代器自己的remove
或add
方法之外,迭代器将抛出ConcurrentModificationException。因此,面对并发修改,迭代器会快速干净地失败,而不会在未来的不确定时间内冒任意,不确定的行为的风险。
如果您不知道,则循环的目标是Iterator
时,for-each循环会在内部使用Iterable
。