之前分析了一些常用的集合,都繞過了迭代器這個概念利朵,這里重點分析一下迭代器相關的知識點。這里首先分析一下ArrayList的迭代器毛俏。
Iterator
首先看一下Iterator的定義:
public interface Iterator<E> {
boolean hasNext();
E next();
void remove();
}
簡單的定義了Iterator的作用幅恋,就是一個迭代器杏死,迭代器最重要的就是兩個方法,判斷是否有寫一個元素hasNext()捆交,和獲取下一個元素next()淑翼。
接下來,會以ArrayList為例品追,介紹Iterator玄括,下面是會以ArrayList為例的介紹Iterator定義:
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;
}
該Iterator繼承了AbstractList的Iterator,其中cursor表示指向下一個元素的游標,lastRet指向上一個放回元素的游標肉瓦,初始情況下遭京,lastRet沒有曾經返回元素,所以初始化為-1泞莉,如圖:
在每次迭代以前首先需要判斷當前是否還有未迭代的元素hasNext:
public boolean hasNext() {
return cursor != size;
}
當游標順序循環(huán)迭代哪雕,迭代完最后一個元素時,游標的位置如圖:
此時的cursor指向數組的位置值恰好等于size戒财,所以此時沒有下一個元素可供迭代提取热监。
接下來看最重要的next()方法:
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];
}
首先checkForComodification()方法是運用了fast-fail機制,后面會單獨分析饮寞;然后:
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
將當前游標值存入到臨時變量i孝扛,并判斷游標值是否大于等于size的大辛泻稹(判斷依據參考上面所說的hasNext()),判斷游標是否越界了苦始;接著:
cursor = i + 1;
return (E) elementData[lastRet = i];
游標右移一位寞钥,同時返回原游標位置的元素,并把lastRet右移一位:
在List中陌选,還定義了一個ListIterator理郑, 他額外實現了向前查找元素,和添加元素的方法咨油,總體來看您炉,在實現原理上可以參考Iterator,這里就不重復分析了
fast-fail
快速失敗機制役电,在上面分析中提到了一個方法checkForComodification()赚爵,運用的就是快速失敗機制,那么在容器的jdk中能夠經撤ㄉ看到以下一段話(摘自arrayList):
* <p>Note that the fail-fast behavior of an iterator cannot be guaranteed
* as it is, generally speaking, impossible to make any hard guarantees in the
* presence of unsynchronized concurrent modification. Fail-fast iterators
* throw {@code ConcurrentModificationException} on a best-effort basis.
* Therefore, it would be wrong to write a program that depended on this
* exception for its correctness: <i>the fail-fast behavior of iterators
* should be used only to detect bugs.</i>
上面大概是說當并發(fā)迭代和結構修改時冀膝,迭代器會盡最大努力拋出 ConcurrentModificationException;具體舉例說霎挟,當線程A獲取當前容器的迭代器后窝剖,線程B對當前容器添加了一個元素(修改元素數據不會改變結構),線程A的跌打器就會檢測到數據結構被變更酥夭,并拋出ConcurrentModificationException赐纱。這就是fast-fail機制。
那么具體實現還是看ArrayList的代碼:
int expectedModCount = modCount;
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
獲取迭代器的時候采郎,迭代器會得到當前容器的modCount修改次數千所,當每次檢查時,只需要看原來的修改次數是否與當前相同蒜埋,就可以知道淫痰,容器是否結構變更了。
for each
在jdk1.5以后整份,提供了對Collection的for each操作待错,通過使用for each的字節(jié)碼可以看到,for each實際是調用了iterator的實現去實現的烈评,這里就不重點分析字節(jié)碼火俄。這邊看一下HashMap的for each的三種實現:
public Set<K> keySet() {
Set<K> ks = keySet;
return (ks != null ? ks : (keySet = new KeySet()));
}
public Collection<V> values() {
Collection<V> vs = values;
return (vs != null ? vs : (values = new Values()));
}
public Set<Map.Entry<K,V>> entrySet() {
return entrySet0();
}
private Set<Map.Entry<K,V>> entrySet0() {
Set<Map.Entry<K,V>> es = entrySet;
return es != null ? es : (entrySet = new EntrySet());
}
分別是遍歷鍵、值讲冠、鍵值對的方式瓜客,很多情況下,容易值遍歷key再取通過可以找value的情況,這樣的話谱仪,就多了一步get的過程玻熙,不如直接通過獲取entryset的效率高,需要注意就是這個情況疯攒。