1. 程式人生 > >HashMap從原始碼角度分析遍歷過程

HashMap從原始碼角度分析遍歷過程

上一篇分析了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的遍歷就分析完了。