为什么ArrayList在使用迭代器迭代元素时不能使用List.remove()删元素,而是使用Iterator.remove()删元素

时间:2024-04-14 12:14:51

其实,我相信有一定经验的都已经知道了,把标题的“为什么”去掉读一遍,但是具体是为什么?今天本人跟进源码(jdk7)探了个究竟。注:这篇文章只介绍使用list.remove()抛出ConcurrentModificationException的原因,其它参数及异常不作介绍。

直入主题,首先我们来看下面这段出问题的代码

为什么ArrayList在使用迭代器迭代元素时不能使用List.remove()删元素,而是使用Iterator.remove()删元素

下面是运行结果

为什么ArrayList在使用迭代器迭代元素时不能使用List.remove()删元素,而是使用Iterator.remove()删元素

使用iterator.remove()是没有问题的,此处就不粘图了

由运行结果可知,抛异常的地方出现在iterator.next(),而且已经遍历完了“bvBody”,当尝试获取“cvBody”时抛了异常。根据异常第一行可知问题出在ArrayList的内部类Itr(也就是代码中的iterator)的checkForComodification()方法,下面是源码

为什么ArrayList在使用迭代器迭代元素时不能使用List.remove()删元素,而是使用Iterator.remove()删元素

简单明了,当modCount != expectdModCount时,抛出并发修改异常(记住这个点)。那么,modCount和expectedModCount分别是什么?modCount在ArrayList中的定义如下,简单理解就是记录改变了ArrayList的size(增删操作)的次数

为什么ArrayList在使用迭代器迭代元素时不能使用List.remove()删元素,而是使用Iterator.remove()删元素

为什么ArrayList在使用迭代器迭代元素时不能使用List.remove()删元素,而是使用Iterator.remove()删元素

expectedModCount在ArrayList的Itr中的定义如下

为什么ArrayList在使用迭代器迭代元素时不能使用List.remove()删元素,而是使用Iterator.remove()删元素

当ArrayList调用iterator()方法,初始化iterator时,将list的modCount值赋给expectedModCount,此时此刻,如果后续expectedModCount和modCount的值不变,或者同步改变保持相等,iterator.next()是不会抛并发修改异常的。既然抛了,它们肯定没有同步修改,直接跟进list.remove()到ArrayList.remove()方法中查看,源码如下

为什么ArrayList在使用迭代器迭代元素时不能使用List.remove()删元素,而是使用Iterator.remove()删元素

跟进fastRemove()源码如下

为什么ArrayList在使用迭代器迭代元素时不能使用List.remove()删元素,而是使用Iterator.remove()删元素

System.arraycopy()效果如下图

为什么ArrayList在使用迭代器迭代元素时不能使用List.remove()删元素,而是使用Iterator.remove()删元素

回到刚才说的list.remove(),方法中modCount自增了,而expectedModCount是没有修改的,因此,在下一次iterator调用next()时,将抛出ConcurrentModificationException。

问题似乎已经弄明白了,不,我们再看看iterator.remove()方法为什么是OK的。如下是其源码

为什么ArrayList在使用迭代器迭代元素时不能使用List.remove()删元素,而是使用Iterator.remove()删元素

首先调用外部类ArrayList的remove()方法,即前面图片中所示,其中修改了modCount。而此处,同时将expectedModCount置为modCount的值,它俩的值再次同步了,所以,iterator的remove()是OK的。

关于可能其它的地方也修改了modCount或expectedModCount的值,在上面代码中,已经确认,除了本文所述的地方存在修改以及list.add()修改了modCount,从迭代开始,是没有其它修改的。

结论:

在使用ArrayList过程中,当使用iterator迭代获取元素并需要移除元素时,需使用iterator的remove()方法移除元素。如果使用list.remove(),将在iterator下一次调用next()时抛出ConcurrentModificationException。如果中途调用list.add(),同样会修改modCount,导致抛出异常。