编程语言
首页 > 编程语言> > 浅析ArrayList源码:Iterator并发修改异常的底层机制

浅析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