先看以下代码
public static void main(String[] args) {
ArrayList<Students> arrayList = new ArrayList<Students>();
arrayList.add(new Students("张三", 23));
arrayList.add(new Students("李四", 24));
arrayList.add(new Students("王五", 24));
Iterator<Students> iterator = arrayList.iterator();
while (iterator.hasNext()) {
Students next = iterator.next();
if (next.getSname().equals("王五")) {
arrayList.remove(next);
}
}
}
运行结果如下
Exception in thread "main" java.util.ConcurrentModificationException
at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1009)
at java.base/java.util.ArrayList$Itr.next(ArrayList.java:963)
at com.czn.collections.ArrayList.IteratorDemo.main(IteratorDemo.java:15)
探究
出现了ArrayList的并发修改异常,为什么会出现这样的异常呢?让我们看看其源码:
(一)add(E e)方法
public boolean add(E e) {
//注意这里的实际修改次数已经是3
//每add一次就会自增1,因为我们写的代码add了三次,
//所以modCount应该是3
modCount++;
add(e, elementData, size);
return true;
}
(二)arrayList.iterator()方法
Iterator iterator = arrayList.iterator();
public Iterator<E> iterator() {
//返回了ArrayList的内部类Itr
return new Itr();
}
再来看看Itr类(先截取一小部分):
这部分主要是是对Itr类的一个成员变量的初始化
private class Itr implements Iterator<E> {
//光标:初始化为0
int cursor;
// 将来所要取出元素的下标,先记录为-1
int lastRet = -1;
//将实际修改次数赋值为期望修改次数
int expectedModCount = modCount;
//Itr类的构造方法
Itr() {}
这里需要注意的地方是expectedModCount也被赋值为3
(三)iterator.hasNext()方法
//这里的代码还是Itr类中的代码
public boolean hasNext() {
return cursor != size;
}
如果光标不等于arrayList的size(注意不是length),说明还没有到最后一个位置,返回true,否则表明到达最后一个位置了,没有下一个了,就返回false。
(四)iterator.next()方法
@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];
}
这里主要注意checkForComodification()方法
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
如果实际修改次数和期望修改次数不一致,就抛出并发修改异常
此时modCount和expectedModCount均等于3,所以还不会抛异常
(四)arrayList.remove()方法
我们的代码在遍历到第三个元素的时候会进入if语句块
if (next.getSname().equals("王五")) {
arrayList.remove(next);
}
进入remove()方法的源码:
public boolean remove(Object o) {
final Object[] es = elementData;
final int size = this.size;
int i = 0;
found: {
if (o == null) {
for (; i < size; i++)
if (es[i] == null)
break found;
} else {
for (; i < size; i++)
if (o.equals(es[i]))
break found;
}
return false;
}
fastRemove(es, i);
return true;
}
//在里面又调用了fastRemove()方法
private void fastRemove(Object[] es, int i) {
//对实际修改次数进行了自增
modCount++;
final int newSize;
if ((newSize = size - 1) > i)
System.arraycopy(es, i + 1, es, i, newSize - i);
es[size = newSize] = null;
}
从以上的源码中可以看出,arrayList.remove()方法也会对进行modCount自增(即modCount ==4),这个时候arrayList的size变为了2
进入while循环iterator.hasNext()
:
public boolean hasNext() {
//这时cursor是3,而size是2,结果返回true
return cursor != size;
}
继续执行下一行代码Students next = iterator.next();
@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];
}
这个方法checkForComodification()的源码在上面已强调:
final void checkForComodification() {
//modCount = 4 而 expectedModCount =3
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
所以在这里抛出了并发修改异常
特殊情况
ArrayList<Students> arrayList = new ArrayList<Students>();
arrayList.add(new Students("张三", 23));
//把修改的元素前移
arrayList.add(new Students("王五", 24));
arrayList.add(new Students("李四", 24));
Iterator<Students> iterator = arrayList.iterator();
while (iterator.hasNext()) {
Students next = iterator.next();
if (next.getSname().equals("王五")) {
arrayList.remove(next);
}
这种情况下不会发生并发修改异常,因为在删除的是第二个元素,那么在删除后
//再次进入while循环
public boolean hasNext() {
//cursor = 2 且 size = 2,返回false
return cursor != size;
}
所以会退出while循环,且元素被成功删除啦,但是不会发生并发修改异常。
不理解的读者可以按照上面的源码过一遍就应该都理解了,可以将两种情况对比起来理解,效果更好哦。
解决方法
ArrayList<Students> arrayList = new ArrayList<Students>();
arrayList.add(new Students("张三", 23));
arrayList.add(new Students("王五", 24));
arrayList.add(new Students("李四", 24));
Iterator<Students> iterator = arrayList.iterator();
while (iterator.hasNext()) {
Students next = iterator.next();
if (next.getSname().equals("王五")) {
iterator.remove();
}
}
可以利用iterator的remove()方法来解决该并发修改异常的问题
//内部类Itr的remove()方法
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
//其底层调用的还是ArrayList的remove()方法
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
//重点在这里,每删除一个元素会把实际修改次数赋值给预期
//修改次数,即两者总是相等的
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
总结
1,集合每次调用add方法的时候,实际修改次数变量的值都会自增一次
2,在获取迭代器的时候,集合只会执行一次将实际修改集合的次数赋值给预期修改集合的次数
3.集合在删除元素的时候也会针对实际修改次数的变量进行自增的操作
4.当删除的元素不是最后一个时,在迭代器的遍历过程中ArrayList.remove(E e)方法删除元素不会发生并发修改异常
5.可以利用Iterator.remove()的方法来解决并发修改异常的问题