浅析ArrayList源码:Iterator并发修改异常的底层机制
作者:互联网
错误信息
ConcurrentModificationException异常
控制台报checkForComodification
常见于迭代器操作中
在ArrayList类中查询“checkForComodification”
在ArrayList中查找关键字,我们首先会找到Itr这个类;
Itr 是ArrayList的内部类,当调用ArrayList的iterator()
方法时,返回的就是一个Itr对象;
可以看到,在Itr迭代器类的 next()
方法里面有一个checkForComodification()
的方法:
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
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];
再找到这个checkForComodification()
方法
final void checkForComodification() {
if (modCount != expectedModCount) //原因是这两个变量不相等
throw new ConcurrentModificationException();
}
可以看到,抛出异常的原因是expectedModCount和modCount 不相等;
expectedModCount是什么?
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
回头再看一下 Itr类,发现expectedModCount是由modCount 赋值的;
继续查找modCount
我发现,ArrayList的很多方法都会对modCount 进行操作;
所有的新增、移出、清空、排序都会使modCount自增:
public void clear() {
modCount++; //使modCount自增
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
public E remove(int index) {
rangeCheck(index);
modCount++; //使modCount自增
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
modCount像是ArrayList的版本号,每当ArrayList做一次修改时,modCount便自增1;
Itr 迭代器在初始化时会获取ArrayList的modCount,赋值给expectedModCount,
为了保证数据的一致性,之后每次调用next()
都会执行一次checkForComodification()
方法,
如果发现当前“版本号”不对,就会抛出ConcurrentModificationException异常。
modCount在哪里?
虽然 ArrayList很多地方用到了modCount,但modCount并没有在ArrayList中被声明;
ArrayList继承了AbstractList,继续向上查找,发现modCount是在AbstractList中被声明的:
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
//AbstractList源码截取
protected transient int modCount = 0;
//......
}
异常原因演示
Iterator<String> iterator =list.iterator(); //初始化迭代器,获取到modCount
while (iterator.hasNext()){
String qwq=iterator.next(); //每次next()验证当前modCount是否更改了
list.remove(2); //集合方法,修改了modCount
// iterator.remove();
//下次next()将会抛出异常
System.out.println(qwq);
}
这里使用iterator.remove()
就不会引起异常;
运行结果:
避免异常的方法
使用迭代器迭代元素时,使用迭代器的方法修改元素;
使用foreach遍历元素时,使用集合的修改元素方法;
为什么迭代器的remove方法不会引起异常?
Itr代码截取:remove()方法
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
Itr的remove()
实际调用的还是集合的remove方法,但是在操作后,重新为expectedModCount 赋值了;
所以,你使用集合的remove()
方法,再手动为expectedModCount 赋值也是可以的(不嫌蛋疼的话);
声明:这里只截取了用于说明的、必要的源码片段,并非完整源码。
标签:index,Iterator,int,ArrayList,remove,modCount,源码,expectedModCount,浅析 来源: https://blog.csdn.net/saltsoul/article/details/97420342