我们先来看下面这个程序(修改集合有增添和删除两种,下面是以增添进行分析,删除同理)
public class ListDemo {
public static void main(String[] args) {
List list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("java");
Iterator it = list.iterator();
while (it.hasNext()) {
String s = it.next();
if ("world".equals(s)) {
list.add("javaee");
}
}
}
}
运行结果如下
Exception in thread "main" java.util.ConcurrentModificationException at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1043) at java.base/java.util.ArrayList$Itr.next(ArrayList.java:997) at com.itheima_02.ListDemo.main(ListDemo.java:22)
二、异常源码分析当我们运行上面程序时,结果会报错ConcurrentModificationException(并发修改异常)
ConcurrentModificationException:当不允许这样的修改时,可以通过检测到对象的并发修改的方法来抛出此异常。
1. 根据终端显示的异常信息,可以看到是第22行String s = it.next();的next()方法报错,而这个next()方法是集合list通过调用iterator()方法产生对应迭代器的方法。我们先跟进一下Iterator
public Iteratoriterator() { return new Itr(); }
2. 可以看到iterator()方法返回一个Iterator实现类对象Itr,再跟进一下Itr类
private class Itr implements Iterator{ int expectedModCount = modCount; @SuppressWarnings("unchecked") public E next() { checkForComodification(); int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
3. 从这个Itr内部类可以找到next()方法,执行next()方法的第一条语句是
checkForComodification();我们再跟进一下这个方法
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
4. 可以看到这个方法做一个判断,判断modCount和expectedModCount是否相等如果不等就会抛出ConcurrentModificationException异常。
modCount:实际集合修改次数
expectedModCount:预期修改次数
5. 我们看2中的Itr类的第一条语句int expectedModCount = modCount;,这条语句保证了modCount与expectedModCount相等,但是当我们调用list的add()方法时就会修改modCount的值,使得二者不相等,再调用next()进行检查时就会抛出并发修改异常。下面看一下add()方法代码,可以看到在调用add()方法时,第一条语句会让modCount++操作,使得modCount与expectedModCount不相等。
public boolean add(E e) {
modCount++;
add(e, elementData, size);
return true;
}
三、集合的三种遍历方式
并发修改异常通常发生在遍历集合时并对其进行修改,遍历集合有三种方式。
☀️ 第一种:普通for循环
【注释】普通for循环边遍历边修改集合不会出现并发修改异常
public class Test implements I {
public static void main(String[] args) {
List list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("javaee");
list.add(1, "python");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}
☀️ 第二种:增强for循环(推荐使用遍历集合的方式)
【注释】增强for循环底层用的也是集合迭代器进行遍历,所以增强for循环和迭代器循环都会触发并发修改异常。
public class Test implements I {
public static void main(String[] args) {
List list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("javaee");
list.add(1, "python");
for (String s : list) {
System.out.println(s);
}
}
}
☀️ 第三种:创建集合迭代器遍历
public class Test implements I {
public static void main(String[] args) {
List list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("javaee");
list.add(1, "python");
Iterator it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
}



