关于Java ConcurrentModificationException异常的解析

日常代码编写的时候不难发现:例如在使用迭代器遍历集合的 同时若进行删改操作,会抛出JavaConcurrent Modification Exce ption 异常,而不使用迭代器则不会出现这个异常,例如下图:
在这里插入图片描述
那么,看起来似乎没有逻辑问题的代码为什么会报错呢?
控制台显示异常出现在chackForComodification()方法中
为探求其本质,博主去查看了底层源代码:
1.进入ArrayList的 iterator方法,在其父类AbstractList中的iterator 返回了一个Itr对象,点击查看具体实现看到了如下代码:


private class Itr implements Iterator<E> {
    int cursor = 0;
    int lastRet = -1;
    int expectedModCount = modCount;
    public boolean hasNext() {
           return cursor != size();
    }
    public E next() {
           checkForComodification();
        try {
        E next = get(cursor);
        lastRet = cursor++;
        return next;
        } catch (IndexOutOfBoundsException e) {
        checkForComodification();
        throw new NoSuchElementException();
        }
    }
    public void remove() {
        if (lastRet == -1)
        throw new IllegalStateException();
           checkForComodification();
 
        try {
        AbstractList.this.remove(lastRet);
        if (lastRet < cursor)
            cursor--;
        lastRet = -1;
        expectedModCount = modCount;
        } catch (IndexOutOfBoundsException e) {
        throw new ConcurrentModificationException();
        }
    }
 
    final void checkForComodification() {
        if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
    }
}

2.其中成员变量:cursor 表示下一个访问元素的索引
lastRet表示上一个访问元素的索引
expectedModCount表示对List修改次数期望
ModCount表示对List修改次数
而如下代码,抛出了这个异常:

if (modCount != expectedModCount)
        throw new ConcurrentModificationException();

3.即当ModCount与expectedModCount的值不等的时候,会出现这个异常,而这两个值,正跟add,remove操作息息相关;
以add()方法为例,查看其源码如下:

 public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

通过对这里的ensureCapacityInternal寻根求源,找到以下源码:
在这里插入图片描述
这个方法底层调用了grow方法来实现,而每次调用remove()或者add()都会使得modCount自增一

4.回到程序中:当使用迭代器iterator引用调用hasNext方法时,其源代码实现很简单:hasNext通过比较下一个访问元素下标是否等于size,若等于则判定其为最后一个元素,copy如下:

public boolean hasNext() {
            return cursor != size;
        }

5.随后进入while中的if判断,iterator引用调用next方法时,进入iterator查询到next方法源码如下:

 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];
        }

在next方法中,调用了这个checkForComodification();而这里正是抛出异常的位置,然而为什么会报错?程序运行到这里,ModCount与expectedModCount的值好像没有变化啊?接着往下分析,当while循环中的if判断到程序需要修改的元素的地方时:执行了一个add方法:al.add(“C”);
ctrl加回车点进去算是大彻大悟了,原来在add方法中调用了
ensureCapacityInternal方法,而正是这个ensureCapacityInternal方法的底层调用使得modCount的值增加了1!

public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

在这里插入图片描述
6.而当下一次next()执行时,在前面调用 checkForComodification()方法时,发现ModCount与expectedModCount的值不等,于是便出现了这个异常
7.在remove方法在也是同样的原因,都是底层方法让ModCount值自增而导致ModCount与expectedModCount的值不等,而抛出异常,则也解释了为什么普通fori并没有这个问题,因为普通fori并不涉及到迭代器,自然不会有这个异常

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值