ConcurrentModificationException并发修改异常
迭代器遍历时,使用集合自带的增删方法时,会报ConcurrentModificationException异常
原因分析
可以通过遍历索引也可以通过迭代器进行遍历。在我们使用迭代器进行遍历集合的时候,会获取到当前集合的迭代对象。在里面有封装了迭代器的remove方法与集合自带的remove方法,如果我们调用迭代器对象的remove方法是没问题的,但是当我们调用集合自带的remove方法时,就会产生ConcurrentModificationException 并发修改异常。也就是说,当我们通过非迭代器进行遍历集合的时候,是不允许集合本身在结构上发生变化的。
增删操作会导致 预期数量和 计数器的数量不一致,就会导致并发修改异常
解决方式
1,ListIterator列表迭代器(list特有的迭代器),将添加、删除元素交给迭代器去做,而非集合,普通迭代器只有remove方法
List<String> list = new ArrayList<>();
list.add("A");list.add("B");list.add("B");list.add("C");
ListIterator<String> stringListIterator = list.listIterator();
while (stringListIterator.hasNext()) {
String element = stringListIterator.next();
if(StringUtils.equals(element,"C")) {
// 在遍历时向列表中添加元素
stringListIterator.add("D");
}
}
System.out.println(list);//[A, B, B, C, D]
2,普通for循环删除时需要从后往前删除,因为从前往后删除可能会漏删(删除第一个B之后,索引到2,但是本来索引2的元素第二个B却来到了索引1的位置)
List<String> list = new ArrayList<>();
list.add("A");list.add("B");list.add("B");list.add("C");
for (int i = list.size()-1; i >= 0; i--) {
if(StringUtils.equals(list.get(i),"B")) {
list.remove("B");
}
}
System.out.println(list);//[A, C]
3,使用CopyOnWriteArrayList集合,底层解决并发修改异常,就可以使用集合的增删方法在遍历时操作
CopyOnWriteArrayList读取时完全不需要加锁,可以在写入的同时读取
当容器需要被修改时,不直接修改当前容器,先将当前容器copy出一个新容器,然后修改新的容器,完成修改后将容器引用指向新的容器,一种读写分离的思想
List<String> list = new ArrayList<>();
list.add("A");list.add("B");list.add("B");list.add("C");
CopyOnWriteArrayList<String> copyOnWriteArrayList = new CopyOnWriteArrayList(list);
Iterator<String> iterator = copyOnWriteArrayList.iterator();
while (iterator.hasNext()){
String next = iterator.next();
if(StringUtils.equals(next,"B")) {
copyOnWriteArrayList.remove("B");
}
}
System.out.println("迭代器遍历时,使用集合增删方法");
System.out.println(copyOnWriteArrayList);//[A, C]
4,当遍历时需要删除元素时,可以使用普通迭代器,普通迭代器没有新增方法
List<String> list = new ArrayList<>();
list.add("A");list.add("B");list.add("B");list.add("C");
//普通迭代器,只能删除,没有add
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
String next = iterator.next();
if(StringUtils.equals(next,"B")) {
//使用迭代器对应remove方法
iterator.remove();//迭代器删除第一个B,然后hasNext拉到下一个B,迭代器再删除一次
}
}
System.out.println(list);//[A, C]
5,增强for循环底层采用迭代器,遍历时删除(新增)只能用集合的删除(新增)方法,还会报并发修改异常
List<String> list = new ArrayList<>();
list.add("A");list.add("B");list.add("B");list.add("C");
for (String element : list) {//Caused by: java.util.ConcurrentModificationException: null
if(StringUtils.equals(element,"B")) {
list.add("B");
}
}