HashMap從原始碼角度分析遍歷過程
阿新 • • 發佈:2019-02-17
上一篇分析了HashMap的資料結構以及put方法的原始碼
HashMap原始碼解析,下面分析HashMap的遍歷過程的原始碼。
遍歷的方法有很多中,主要分析下面這種:
Iterator<Map.Entry<String, String>> iterator = hashMap.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, String> next = iterator.next();
String key = next.getKey();
String value = next.getValue();
}
hashMap.entrySet()會new一個EntrySet物件。
呼叫entrySet.iterator()方法時會通過newEntryIterator()方法呼叫HashIterator的構造方法建立一個Iterator例項。
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());
}
private final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
public Iterator<Map.Entry<K,V>> iterator() {
return newEntryIterator();
}
//..............
}
Iterator<Map.Entry<K,V>> newEntryIterator() {
return new EntryIterator();
private abstract class HashIterator<E> implements Iterator<E> {
HashMapEntry<K,V> next; // next entry to return
int expectedModCount; // For fast-fail
int index; // current slot
HashMapEntry<K,V> current; // current entry
HashIterator() {
expectedModCount = modCount;
if (size > 0) { // advance to first entry
HashMapEntry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
}
public final boolean hasNext() {
return next != null;
}
final Entry<K,V> nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
HashMapEntry<K,V> e = next;
if (e == null)
throw new NoSuchElementException();
if ((next = e.next) == null) {
HashMapEntry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
current = e;
return e;
}
public void remove() {
if (current == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Object k = current.key;
current = null;
HashMap.this.removeEntryForKey(k);
expectedModCount = modCount;
}
}
HashIterator的構造方法通過while迴圈遍歷HashMap中的陣列,找到第一個不為null的值,並賦值給next。
PS: HashMap中取資料是從陣列的第0個位置開始找的,根據上篇分析put方法時,知道插入的位置時根據hash值計算出來的,所以HashMap取出資料的順序和插入的順序是不一致的。
HashIterator() {
expectedModCount = modCount;
if (size > 0) { // advance to first entry
HashMapEntry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
}
通過hashMap.entrySet().iterator()已經找到了第一個不為null的元素,如果hasNext()返回false表示沒有別的元素,返回true呼叫iterator.next()方法,此方法最終呼叫nextEntry()。
nextEntry將構造方法中找到的next返回,並通過next節點中儲存的下一個節點的指標去找下個數據,如果下一個資料為null,則表示該連結串列已經到最尾部了,則繼續查詢陣列下一個位置儲存的連結串列,直到不為null,或者遍歷結束。
public final boolean hasNext() {
return next != null;
}
final Entry<K,V> nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
//將next 賦值給e 當作返回值使用
HashMapEntry<K,V> e = next;
if (e == null)
throw new NoSuchElementException();
//連結串列結構儲存了下個節點的指標,可以直接取
if ((next = e.next) == null) {
//當前節點是連結串列中最後一個元素,從陣列下一個位置中查詢不為null的元素
HashMapEntry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
current = e;
return e;
}
呼叫hashMap.get(“4”);取資料,流程和呼叫put方法儲存資料相同,都是通過key的值得到hash值然後找到陣列中的位置,之後遍歷連結串列得到Entry。找到Entry之後呼叫getKey和getValue就非常簡單了。
final Entry<K,V> getEntry(Object key) {
if (size == 0) {
return null;
}
int hash = (key == null) ? 0 : sun.misc.Hashing.singleWordWangJenkinsHash(key);
for (HashMapEntry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
return null;
}
這裡HashMap的遍歷就分析完了。