ArrayList什么情况会抛出ConcurrentModificationException

背景

近日,在看ArrayList的源码实现,发现很多情况会抛出ConcurrentModificationException。下面总结一下大致发生的情况。 首先,ArrayList不是线程安全的。 首先来看一个例子:

public  static  void main(String[] args){
        List<Integer> aList = new ArrayList<Integer>();
        aList.add(1);
        aList.add(2);
        
       Iterator<Integer> iter = aList.iterator();
       aList.add(3);
       System.out.println(iter.next());
   }

运行结果:

Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
	at java.util.ArrayList$Itr.next(ArrayList.java:831)
	at com.zhu.util.ArrayIistIteratorTest.main(ArrayIistIteratorTest.java:19)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)

由例子可见,在调用iterator方法后,如果其他线程对ArrayList进行了更改大小的操作,如add和remove。那么将会抛出ConcurrentModificationException。字面意思很简单:并发修改异常。

这种情况也会出现

List<String> list = new ArrayList<String>();
list.add("A");
list.add("B");
list.add("C");
 
for (String s : list) {
    if (s.equals("B")) {
        list.remove(s);
    }
}

运行后
在这里插入图片描述

分析

我们通过源码,来看看为什么会出现这样的情况: 首先ArrayList的iterator方法实现:

public Iterator<E> iterator() {
        return new Itr();
    }

Itr是ArrayList的内部类,实现了Iterator接口,下面是Itr的源码:

private class Itr implements Iterator<E> {
        /**
         * Index of element to be returned by subsequent call to next.
         */
        int cursor = 0;

        /**
         * Index of element returned by most recent call to next or
         * previous.  Reset to -1 if this element is deleted by a call
         * to remove.
         */
        int lastRet = -1;

        /**
         * The modCount value that the iterator believes that the backing
         * List should have.  If this expectation is violated, the iterator
         * has detected concurrent modification.
         */
        int expectedModCount = modCount;

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

        public E next() {
            checkForComodification();
            try {
                int i = cursor;
                E next = get(i);
                lastRet = i;
                cursor = i + 1;
                return next;
            } catch (IndexOutOfBoundsException e) {
                checkForComodification();
                throw new NoSuchElementException();
            }
        }

        public void remove() {
            if (lastRet < 0)
                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();
        }
    }

Itr只有三个成员变量:cursor,lastRet,exceptdModCount

  • cursor:next方法应该返回元素所在的下标
  • lastRet:上一次next返回元素的下标
  • exceptedModCount:ArrayList成员变量modCount的副本 ArrayList中modCount的意义是记录

当前ArrayList被修改的次数,ArrayList中会引起modCount发生改变的方法有以下集中:

  • trimToSize:将数组大小减小至当前元素个数
  • ensureCapacity:容量设置
  • add:新增元素的时候
  • remove:移除元素的时候
  • clear:清空的时候 所有移除和新增的操作都会引起modCount的修改

可以看出引起例子抛出异常的原因是因为Itr创建之后,exceptedModCount为当时ArrayList对象modCount的值。在Itr的next和remove方法中可以看出,在世纪操作之前都会调用checkForComodification来检查ArrayList是否被修改过。在调用Itr中next和remove与Itr创建之间,如果有其他线程或本线程调用了引起ArrayList的modCount发生变化的操作,那么将会抛出并发修改异常。

那么下面我们再来看看还有其他什么情况,ArrayList会抛出CurrentModificationException。

  • writeObject:writeObject是序列化时调用的方法,也就是说在在元素序列化时,如果有其他操作引起了modCount发生改变时会抛出并发修改异常。
  • Itr的next和remove:在使用迭代器期间,其他操作引起modCount改变时。
  • ListItr的previous,next,set,remove,add:同Itr.
  • SubList中的所有操作

解决方案

方案一

Iterator<String> iter = list.iterator();
while(iter.hasNext()){
    String str = iter.next();
    if( str.equals("B")){
        iter.remove();
    }
}

方案二

List<String> list = new CopyOnWriteArrayList<String>();
list.add("A");
list.add("B");
list.add("C");
 
for (String s : list) {
    if (s.equals("B")) {
        list.remove(s);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值