为何会发生java.util.ConcurrentModificationException?

相信很多初学的朋友都遇到过这个异常,但是又不知道怎么回事,今天我给大家说一下,通过解读源码来解析这个异常是怎样发生的,我们先写一段代码:

public static void main(String[] args) {
		List<String> list = new ArrayList<String>();
		list.add("hello");
		list.add("world");
		list.add("!");
		list.add("extra");
		for(String str:list){
			System.out.println(str);
			list.remove(str);//主要看这里
		}

	}
这段代码就是遍历集合,然后把集合的元素移除,大家可以看到,这个方法在ArrayList类里面是有的,但是为什么遍历的时候会提示这个异常呢?先看一下异常,异常信息如下:

Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
	at java.util.ArrayList$Itr.next(Unknown Source)
	at test.ConcurrentModifiedExceptionTest.main(ConcurrentModifiedExceptionTest.java:14)

注意看后面的内部类Itr,说明异常是在Itr这个类抛出来的,奇怪了,明明我们调用的是list的remove方法,为什么和Itr有关呢?其实这个就和java的语法糖有关了,先不说语法糖,我们先看一下Itr这个内部类,然后找到checkForComodification方法,可能你会发现有几个地方定义了,我们要看的是Itr类的方法,如下:

final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

非常简单的一个方法,modCount不等于expectedModCount的时候就抛出这个异常了,出错的位置找到了,但是为什么出错似乎还没找到,接着看,因为我们最终需要将java文件编译成class文件,执行的时候也是执行class文件,那么也就是我们写的代码被编译成class文件的时候用到了Itr这个内部类,反编译一下这个类看一下反编译之后的代码是怎样的,可能不同的反编译器会有不同的结果,但是本质上应该是一样的,因为一般是没办法还原语法糖的(或许可以可能是我对jvm了解不够),因为编译的时候语法糖就被去掉了,除非class文件中包含还原语法糖的信息,代码如下:

public static void main(String[] args) {
        ArrayList list = new ArrayList();
        list.add("hello");
        list.add("world");
        list.add("!");
        list.add("extra");
        Iterator var3 = list.iterator();

        while(var3.hasNext()) {
            String j = (String)var3.next();
            list.remove(j);
        }

可以看到红色的代码就是我们的源码被编译之后的代码(当然这个是我们反编译之后的,class文件内容是字节码),也就是我们使用foreach语法之后会被编译器进行语法糖解析最终的使用的是iterator的方式进行遍历的,红色代码第一句返回的就是一个Itr类的实例:

public Iterator<E> iterator() {
        return new Itr();
    }
关键是最后一句,调用list的remove方法的时候里面有一句 modCount ++;这句改变了这个值,但是expectedModCount这个值并没有变,所以如果使用foreach的方式进行编码的时候记得要调用iterator的remove方法,否则就会抛异常。可能你写成以下这样了:

Iterator<String> it = list.iterator();
		while(it.hasNext()){
			it.remove();
		}

但是你发现还是会抛异常,只是异常信息不一样了,明明也是调用了iterator的remove方法啊,怎么还是异常?看了源码会发现remove方法的第一句就是判断lastRet是否小于0,初始化就是-1,所以肯定会抛异常,那么应该怎么做?很简单,就调用一下next()方法就ok了。当然一般情况下我们都会调用next方法的,这里只是说明一下。














评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值