异常名称:
ConcurrentModificationException并发修改异常
问题描述
当调用了集合本身增删过程中出现并发修改异常:
当我们在遍历实现了collection接口与iterator 接口的集合时(List、Set、Map), 我们可以通过遍历索引也可以通过迭代器进行遍历。在我们使用迭代器进行遍历集合的时候,会获取到当前集合的迭代对象。在里面有封装了迭代器的remove方法与集合自带的remove方法,如果我们调用迭代器对象的remove方法是没问题的,但是当我们调用集合自带的remove方法时,就会产生ConcurrentModificationException 并发修改异常。也就是说,当我们通过非迭代器进行遍历集合的时候,是不允许集合本身在结构上发生变化的。
举例说明-代码
案例演示需求:
//案例演示需求:
//我有一个集合,请问,我想判断里面有没有"world"这个元素,如果有,我就添加一个"javaee"元素,请写代码实现。
List list = new ArrayList();
list.add("a");
list.add("b");
list.add("world");
list.add("d");
list.add("e");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
System.out.println("--------------");
for (Object o : list) {
if (o.equals("a")){
list.remove(o);
}
}
// 以为为map的例子
TreeMap<Car, String> treeMap = new TreeMap<>();
//添加元素
treeMap.put(new Car("奔驰", 1200000), "zhangsan");
treeMap.put(new Car("宝马", 800000), "lisi");
treeMap.put(new Car("三菱", 50000), "zhaoliu");
treeMap.put(new Car("三8菱", 500), "zhaoliu");
treeMap.put(new Car("丰田", 150000), "wangwu");
for (com.dljd.day12.user.HomeWork01.Car car : treeMap.keySet()) {
System.out.println(car);
}
//TODO
System.out.println("---------------------------------");
for (com.dljd.day12.user.HomeWork01.Car car : treeMap.keySet()) {
if (car.price < 100000)
treeMap.remove(car);
}
for (com.dljd.day12.user.HomeWork01.Car car : treeMap.keySet()) {
System.out.println(car);
}
public class Car implements Comparable<Car> {
String carName;
int price;
public Car(String carName, int price) {
this.carName = carName;
this.price = price;
}
public Car() {
}
@Override
public String toString() {
return "Car{" +
"carName='" + carName + '\'' +
", price=" + price +
'}';
}
@Override
public int compareTo(Car car) {
return car.price - this.price;
}
原因分析:
根据追踪源码发现,并发修改异常是因为,在集合中增删操作会导致
预期数量
和计数器的数量
不一致,就会导致并发修改异常
解决方案:
1.普通for循环可解决
List list = new ArrayList();
list.add("a");
list.add("b");
list.add("world");
list.add("d");
list.add("e");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
System.out.println("--------------");
for (int i = 0; i < list.size(); i++) {
if (list.get(i).equals("a")) {
list.remove(i);
}
System.out.println(list.get(i));
}
2.list集合有一个listIterator的迭代器可以进行元素添加
package com.heima.list;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class Demo03_List {
/**
* * A:案例演示
* 需求:我有一个集合,请问我想判断里面有没有"world"这个元素.如果有,我就添加一个"javaee"元素,请写代码实现.
*/
public static void main(String[] args) {
List list = new ArrayList();
list.add("a"); //Object obj = new String();
list.add("b");
list.add("world");
list.add("c");
list.add("d");
list.add("e");
/*Iterator it = list.iterator(); //获取迭代器
while (it.hasNext()) { //判断集合中是否有元素
String str = (String) it.next(); //向下转型
if ("world".equals(str)) {
list.add("javaee"); //遍历的同时在增加元素,并发修改ConcurrentModificationException
}
}*/
ListIterator lit = list.listIterator(); //获取迭代器(List集合特有的)
while (lit.hasNext()) {
String str = (String)lit.next(); //向下转型
if ("world".equals(str)) {
//list.add("javaee"); //遍历的同时在增加元素,并发修改ConcurrentModificationException
lit.add("javaee");
}
}
System.out.println(list);
}
}
特例:
当删除list集合中倒数第二个元素时,不会报出并发修改异常
,但仅仅适用于list,map集合依然会报出并发修改异常
public static void main(String[] args) {
List<String> list = new ArrayList<>();
// LinkedList<String> list = new LinkedList<>();
/**
* list执行add的时候,计数器modCount++ 自增(从0开始)(集合不管增或者删计数器都会自增+1)
*/
list.add("1");
list.add("3");
list.add("8");
//此时计数器modCount=2
for (String s : list) {
/**
* 增强for循环,for和while都是关键字,编译的时候,会使用迭代器,底层是迭代器,Iterater
* list集合实现类ArrayList中初始化迭代器的时候,会把计数器modCount赋值给迭代器的计数器
* expectedModCount = modCount
* 每次获取下一个元素的时候,先判断if (modCount != expectedModCount),如果不相等,则抛出并发修改异常
* ConcurrentModificationException
*/
if (s.equals("3")) {
list.remove(s);
}
/**
* 特例:当删除集合中倒数第二个元素时
* 在循环中删除1的时候没有抛出并发修改异常
* 因为,每次进行下一次循环的时候,调用hasNext方法,判断是否有下一个元素。
* 根据当前遍历的索引位置和集合长度比较,如果索引位置等于集合长度了,表示遍历完了(否则就会数组越界)
* 因为删除0索引位置的数据,集合长度是1,索引位置也是1,于是相等,循环结束。
*/
}
for (String s : list) {
System.out.println(s);
}
}