在我的上一篇文章 Java中三种遍历Collection中元素的方法Iterator、forEach、for循环对比 中提到Iterator和forEach循环在遍历Collection中元素时最大的差别就是在方法remove()
上,由于在Iterator的remove()
方法中维护一个标志位,所以删除元素时不会出现异常,所以本篇文章就深入Collection与Iterator的源码看看内部究竟是如何实现的。
一. Collection及其实现类ArrayList的部分源码
1.Collection内部源码
首先我们来看一下Collection内部源码(为方便分析,此处只展示与本篇文章有关的部分):
可以看到Collection是一个接口,内部定义了remove()
与iterator()
方法。
2.ArrayList内部源码
由于Collection接口内部无具体实现,所以我们来看Collection的一个最常用的实现类ArrayList内部源码(为方便分析,此处只展示与本篇文章有关的部分):
在ArrayList中并没有直接实现Collection接口,而是通过继承AbstractList抽象类,而AbstractList抽象类又继承了AbstractCollection抽象类,最终AbstractCollection抽象类实现Collection接口;所以ArrayList间接实现了Collection接口,有兴趣的大佬可以自己去研究下为什么这样子设计,在这里就不多加讨论。
可以看到在ArrayList中有实现remove()
与iterator()
方法,并且通过iterator()
方法得到的内部类Itr实现了Iterator接口,在Itr内部类中也有实现remove()
方法,下面就来具体的探讨其中的区别。
二. ArrayList的remove()方法分析
1.remove()方法
在ArrayList的remove()
方法内部的实现主要是通过循环找到元素的下标, 然后调用私有的fastRemove()
方法:
remove()
方法没啥好讲的,关键在于调用的fastRemove()
方法上。
2.fastRemove()方法
fastRemove()方法中会先修改modCount的值,然后将通过复制一个新的数组的方法将原来index位置上的值覆盖掉,最后数组大小减一。我们重点关注fastRemove()
方法的第一行代码:
也就是每次调用remove()
方法都会使modCount的值加一。那么modCount变量又是什么呢?
3.modCount变量
modCount在ArrayList中没有定义,是在ArrayList的父类AbstractList抽象类中定义的:
modCount的作用是记录操作(添加删除)ArrayList中元素的次数(这个很关键),每次操作ArrayList中元素后就会使modCount加一。
三. Iterator的remove()方法分析
看源码可知ArrayList通过iterator()
方法得到了一个内部类Itr,这个内部类实现了Iterator接口,我们重点分析内部类Itr中的实现。
1.expectedModCount 变量
在内部类Itr中定义了一个变量expectedModCount :
expectedModCount 只在new一个Itr对象时初始化为modCount
2.next()与remove()方法
在调用Itr对象的next()
与remove()
方法时第一步会先调用checkForComodification()
方法。
并且在remove()
方法中会调用ArrayList.this.remove(lastRet)
方法(也就是具体的ArrayList对象的remove()方法,上面我们讲过,在ArrayList对象的remove()方法中会使得modCount的值加一),然后修改expectedModCount 的值为modCount。
3.checkForComodification()方法
checkForComodification()
会检查expectedModCount与modCount 是否相等,如果不相等就会抛出ConcurrentModificationException异常。
四. 总结
通过上面的分析我们可以得出,Collection与Iterator的remove()方法最大的区别就是:
Iterator的remove()方法会在删除元素后将modCount 的值赋值给expectedModCount,使其又相等。
1.如果我们在Iterator循环中调用Collection的remove()方法
|
|
由于collection.remove(obj )只会删除obj元素后将modCount 的值加一,并不会修改expectedModCount的值,所以当下一次调用it.next()方法时发现modCount != expectedModCount,将抛出ConcurrentModificationException异常。
2.如果我们在Iterator循环中调用Iterator的remove()方法
|
|
由于it.remove(obj )会在删除obj元素后将modCount 的值加一,并将expectedModCount重新赋值为modCount ,使其相等,所以当下一次调用it.next()方法时发现modCount == expectedModCount,正常执行。