白話HashMap源碼(下)

上一篇白話HashMap源碼(上)看過了HashMap的基本結構和主要的操作方法Get、Put、Remove的源碼。瞭解了HashMap的存儲方式,數組加鏈表,與空間的自增長思路,每次空間翻倍舊數據重新裝填。

這一篇主要看一下遍歷相關的源碼。

遍歷前需要了解的方法

containsKey

    @Override public boolean containsKey(Object key) {
        if (key == null) {//key爲null的條件
            return entryForNullKey != null;
        }

        int hash = Collections.secondaryHash(key);
        HashMapEntry<K, V>[] tab = table;
        for (HashMapEntry<K, V> e = tab[hash & (tab.length - 1)];
                e != null; e = e.next) {//由key找到數組內的具體項,遍歷鏈表
            K eKey = e.key;
            if (eKey == key || (e.hash == hash && key.equals(eKey))) {
                return true;
            }
        }
        return false;
    }

containsValue

橫向遍歷數組,縱向遍歷鏈表,如果沒有找到別忘了HashMap還維護了一個entryForNullKey。

但是區分了value爲null的條件,因爲null.equals會報錯。

    @Override public boolean containsValue(Object value) {
        HashMapEntry[] tab = table;
        int len = tab.length;
        if (value == null) {
            for (int i = 0; i < len; i++) {
                for (HashMapEntry e = tab[i]; e != null; e = e.next) {
                    if (e.value == null) {
                        return true;
                    }
                }
            }
            return entryForNullKey != null && entryForNullKey.value == null;
        }

        // value is non-null
        for (int i = 0; i < len; i++) {
            for (HashMapEntry e = tab[i]; e != null; e = e.next) {
                if (value.equals(e.value)) {
                    return true;
                }
            }
        }
        return entryForNullKey != null && value.equals(entryForNullKey.value);
    }

遍歷keySet

由於keySet、values、entrySet的實現邏輯一樣,此處就只看keySet的源碼

第一步返回Set

    @Override public Set<K> keySet() {
        Set<K> ks = keySet;
        return (ks != null) ? ks : (keySet = new KeySet());
    }

可以看到返回了一個KeySet對象

    private final class KeySet extends AbstractSet<K> {
        public Iterator<K> iterator() {
            return newKeyIterator();
        }
        public int size() {
            return size;
        }
        public boolean isEmpty() {
            return size == 0;
        }
        public boolean contains(Object o) {
            return containsKey(o);
        }
        public boolean remove(Object o) {
            int oldSize = size;
            HashMap.this.remove(o);
            return size != oldSize;
        }
        public void clear() {
            HashMap.this.clear();
        }
    }

裏面包含了常用的方法,size方法直接用HashMap對象的size,isEmpty一樣,contains則是使用HashMap的containsKey方法,remove也是調用HashMap對象的方法,關鍵的是iterator這個迭代器。

第二步迭代器

Iterator<K> newKeyIterator() { return new KeyIterator();   }

源碼很簡單,直接返回KeyIterator對象,繼續向下走。

    private final class KeyIterator extends HashIterator
            implements Iterator<K> {
        public K next() { return nextEntry().key; }
    }

返回了nextEntry.key,看名字應該是下一項,但nextEntry哪裏來的呢?

看KeyIterator 的繼承,HashIterator它是整個HashMap迭代的實現者

第三步迭代的實現者

    private abstract class HashIterator {
        int nextIndex;
        HashMapEntry<K, V> nextEntry = entryForNullKey;
        HashMapEntry<K, V> lastEntryReturned;
        int expectedModCount = modCount;

        HashIterator() {//初始時,nextEntry指到第一項
            if (nextEntry == null) {
                HashMapEntry<K, V>[] tab = table;
                HashMapEntry<K, V> next = null;
                while (next == null && nextIndex < tab.length) {
                    next = tab[nextIndex++];
                }
                nextEntry = next;
            }
        }

        public boolean hasNext() {
            return nextEntry != null;
        }

        HashMapEntry<K, V> nextEntry() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            if (nextEntry == null)
                throw new NoSuchElementException();

            HashMapEntry<K, V> entryToReturn = nextEntry;
            HashMapEntry<K, V>[] tab = table;
            HashMapEntry<K, V> next = entryToReturn.next;
            //如果這一項是鏈表,則返回鏈表的下一項,否則返回數組的下一項
            while (next == null && nextIndex < tab.length) {
                next = tab[nextIndex++];
            }
            nextEntry = next;
            return lastEntryReturned = entryToReturn;
        }

        public void remove() {
            if (lastEntryReturned == null)
                throw new IllegalStateException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            HashMap.this.remove(lastEntryReturned.key);
            lastEntryReturned = null;
            expectedModCount = modCount;
        }
    }

至此HashMap的源碼就看完了,至於其他功能類的方法,相信有了這兩篇的基礎想看懂原理應該不難。

HashSet的內部實現其實也是HashMap,只是HashSet存入的value作爲key,HashSet對象作爲value存入HashMap。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章