在我的上一篇文章 Java中三種遍歷Collection中元素的方法(Iterator获茬、forEach牵咙、for循環(huán))對比 中提到Iterator和forEach循環(huán)在遍歷Collection中元素時最大的差別就是在方法remove()
上,由于在Iterator的remove()
方法中維護一個標志位,所以刪除元素時不會出現(xiàn)異常鳖昌,所以本篇文章就深入Collection與Iterator的源碼看看內(nèi)部究竟是如何實現(xiàn)的胖眷。
一. Collection及其實現(xiàn)類ArrayList的部分源碼
1.Collection內(nèi)部源碼
首先我們來看一下Collection內(nèi)部源碼(為方便分析恕酸,此處只展示與本篇文章有關(guān)的部分):
public interface Collection<E> extends Iterable<E> {
boolean remove(Object o);
Iterator<E> iterator();
/**
* 此處省去其他方法定義
*/
}
可以看到Collection是一個接口榕栏,內(nèi)部定義了remove()
與iterator()
方法畔勤。
2.ArrayList內(nèi)部源碼
由于Collection接口內(nèi)部無具體實現(xiàn),所以我們來看Collection的一個最常用的實現(xiàn)類ArrayList內(nèi)部源碼(為方便分析扒磁,此處只展示與本篇文章有關(guān)的部分):
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
/* -----------------------我是便于觀察的分割線----------------------- */
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
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];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
/**
* 此處省去其他方法定義
*/
}
在ArrayList中并沒有直接實現(xiàn)Collection接口庆揪,而是通過繼承AbstractList抽象類,而AbstractList抽象類又繼承了AbstractCollection抽象類妨托,最終AbstractCollection抽象類實現(xiàn)Collection接口缸榛;所以ArrayList間接實現(xiàn)了Collection接口,有興趣的大佬可以自己去研究下為什么這樣子設(shè)計兰伤,在這里就不多加討論内颗。
可以看到在ArrayList中有實現(xiàn)remove()
與iterator()
方法,并且通過iterator()
方法得到的內(nèi)部類Itr實現(xiàn)了Iterator接口敦腔,在Itr內(nèi)部類中也有實現(xiàn)remove()
方法均澳,下面就來具體的探討其中的區(qū)別。
二. ArrayList的remove()方法分析
1.remove()方法
在ArrayList的remove()
方法內(nèi)部的實現(xiàn)主要是通過循環(huán)找到元素的下標符衔, 然后調(diào)用私有的fastRemove()
方法:
fastRemove(index);
remove()
方法沒啥好講的找前,關(guān)鍵在于調(diào)用的fastRemove()
方法上。
2.fastRemove()方法
fastRemove()方法中會先修改modCount的值柏腻,然后將通過復制一個新的數(shù)組的方法將原來index位置上的值覆蓋掉纸厉,最后數(shù)組大小減一。我們重點關(guān)注fastRemove()
方法的第一行代碼:
modCount++;
也就是每次調(diào)用remove()
方法都會使modCount的值加一五嫂。那么modCount變量又是什么呢?
3.modCount變量
modCount在ArrayList中沒有定義肯尺,是在ArrayList的父類AbstractList抽象類中定義的:
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
protected transient int modCount = 0;
/**
* 此處省去其他方法定義
*/
}
modCount的作用是記錄操作(添加刪除)ArrayList中元素的次數(shù)(這個很關(guān)鍵)沃缘,每次操作ArrayList中元素后就會使modCount加一。
三. Iterator的remove()方法分析
看源碼可知ArrayList通過iterator()
方法得到了一個內(nèi)部類Itr则吟,這個內(nèi)部類實現(xiàn)了Iterator接口槐臀,我們重點分析內(nèi)部類Itr中的實現(xiàn)。
1.expectedModCount 變量
在內(nèi)部類Itr中定義了一個變量expectedModCount :
int expectedModCount = modCount;
expectedModCount 只在new一個Itr對象時初始化為modCount
2.next()與remove()方法
在調(diào)用Itr對象的next()
與remove()
方法時第一步會先調(diào)用checkForComodification()
方法氓仲。
checkForComodification();
并且在remove()
方法中會調(diào)用ArrayList.this.remove(lastRet)
方法(也就是具體的ArrayList對象的remove()方法水慨,上面我們講過得糜,在ArrayList對象的remove()方法中會使得modCount的值加一),然后修改expectedModCount 的值為modCount晰洒。
3.checkForComodification()方法
checkForComodification()
會檢查expectedModCount與modCount 是否相等朝抖,如果不相等就會拋出ConcurrentModificationException異常。
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
四. 總結(jié)
通過上面的分析我們可以得出谍珊,Collection與Iterator的remove()方法最大的區(qū)別就是:
Iterator的remove()方法會在刪除元素后將modCount 的值賦值給expectedModCount治宣,使其又相等。
1.如果我們在Iterator循環(huán)中調(diào)用Collection的remove()方法
public static void display(Collection<Object> collection) {
Iterator<Object> it = collection.iterator();
// 會拋出ConcurrentModificationException異常
while(it.hasNext()) {
Object obj = it.next();
collection.remove(obj );
}
}
由于collection.remove(obj )只會刪除obj元素后將modCount 的值加一砌滞,并不會修改expectedModCount的值侮邀,所以當下一次調(diào)用it.next()方法時發(fā)現(xiàn)modCount != expectedModCount,將拋出ConcurrentModificationException異常贝润。
2.如果我們在Iterator循環(huán)中調(diào)用Iterator的remove()方法
public static void display(Collection<Object> collection) {
Iterator<Object> it = collection.iterator();
// 正常執(zhí)行
while(it.hasNext()) {
Object obj = it.next();
it.remove(obj );
}
}
由于it.remove(obj )會在刪除obj元素后將modCount 的值加一绊茧,并將expectedModCount重新賦值為modCount ,使其相等打掘,所以當下一次調(diào)用it.next()方法時發(fā)現(xiàn)modCount == expectedModCount华畏,正常執(zhí)行。