迭代器Iterator原始碼分析
我們在遍歷一個集合物件時經常會使用到迭代器Iterator,它提供給我們獲取、刪除集合中元素的功能。Iterator包含三個方法,分別是:
1、hasNext(),用於判斷迭代器是否已從頭到尾將集合遍歷了一遍,後面是否還有元素。
2、next(),用於獲取當前指向的元素。
3、remove(),刪除剛剛訪問過的元素。
接下來簡單講一下Iterator與抽象類AbstractList,介面List、Collection的關係,介面Collection繼承了介面Iterable,從而覆蓋了Iterable介面中的方法iterator(),而介面List繼承了Collection,接著AbstractList抽象類實現了介面List,我們看看AbstractList類中對方法iterator()的定義:
public Iterator<E> iterator() {
return new Itr();
}
該方法返回了一個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 { E next = get(cursor); lastRet = cursor++; return next; } catch (IndexOutOfBoundsException e) { checkForComodification(); throw new NoSuchElementException(); } } public void remove() { if (lastRet == -1) 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類實現了介面Iterator,對hasNext(),next(),remove()三個方法進行了定義,首先來看看類的三個屬性。
用於記錄當前迭代器遍歷到的位置,通過該屬性訪問元素
int cursor = 0;
該屬性用於記錄上一次使用next()方法時遍歷所在的位置,在呼叫remove方法後該屬性值變回-1
int lastRet = -1;
modCount是ArrayList的屬性,用於記錄對集合進行操作的次數,包括add、remove等方法。expectedModCount的應用是為了實現一種”快速失敗"機制,即在使用迭代器的過程中如果對原集合(如ArrayList)進行了修改,此時modCount的值將會發生變化,二者不再相同,此時將會丟擲併發修改異常。ConcurrentModificationException int expectedModCount = modCount;
理解了幾個屬性之後我們再來看看三個方法:
1、hasNext()
public boolean hasNext() {
return cursor != size();
}
呼叫集合的size()方法,返回集合大小,通過將現階段訪問位置與集合大小進行對比判斷迭代器後面是否還有可遍歷的元素。
2、next()
public E next() {
checkForComodification();
try {
E next = get(cursor);
lastRet = cursor++;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
首先判斷是否發生併發修改異常,不是的話就呼叫get方法獲取cursor指向位置的值,將cursor賦值給lastRet,cursor自動加1.返回獲取到的物件。
3、remove()
public void remove() {
if (lastRet == -1)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
該方法首先判斷lastRet的值,如果等於-1,表示剛剛進行了刪除操作,此時丟擲不合法狀態異常,接著再判斷是否丟擲併發修改異常,如果不,則呼叫集合的remove方法對lastRet指向位置的集合元素進行刪除,再判斷lastRet是否小於cursor,因為集合進行了刪除操作,元素位置發生了變化,這一步是為了通過改變cursor的值改變迭代器指向的位置。同時,lastRet值設為-1,表示剛剛進行了刪除操作,expectedModCount屬性的值也進行修改,等於 modCount,以防丟擲同步修改異常。