01_HashMap實(shí)現(xiàn)原理

  • 基于Java8
    HashMap的doc中寫道:

Hash table based implementation of the Map interface. This implementation provides all of the optional map operations, and permits null values and the null key. (The HashMap class is roughly equivalent to Hashtable, except that it is unsynchronized and permits nulls.) This class makes no guarantees as to the order of the map; in particular, it does not guarantee that the order will remain constant over time.

HashMap 是基于Hashtable的實(shí)現(xiàn),The HashMap class is roughly equivalent to Hashtable,但是和Hashtable不同的是,HashMap支持null鍵和null value,另外HashMap是非線程安全的類。

在整體結(jié)構(gòu)上,HashMap和Hashtable是一致的,即為數(shù)組和鏈表的合成結(jié)構(gòu),恰好利用了二者的優(yōu)勢:數(shù)組易于尋址而不易于插入和刪除元素,鏈表不易于尋址但易于插入和刪除元素。

接下來看看實(shí)現(xiàn)上的某些有意思的細(xì)節(jié)。

  1. 低層數(shù)組初始化
    和Hashtable不同的是,HashMap的無參構(gòu)造器沒有初始化Node數(shù)組(Hashtable中是Entry數(shù)組),而是把初始化延遲到了第一次put數(shù)據(jù)的時(shí)候,如代碼:
/**
     * Constructs an empty <tt>HashMap</tt> with the default initial capacity
     * (16) and the default load factor (0.75).
     */
    public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }

只是初始化了loadFactor,table對象為null。

然后在put方法中,resize()才開始初始化Node數(shù)組:

    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        // 無參構(gòu)造器初始化后table是null,就會進(jìn)入resize()方法進(jìn)行動態(tài)擴(kuò)容
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        // 這個取余的方法很有意思 在這里(n - 1) & hash是等價(jià)于hash % n的 ,因?yàn)樵谶@里n必定是2的n次方。
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        // 這里有一個將鏈表轉(zhuǎn)換成紅黑樹的方法,這樣可以提高查詢的效率
                        //  配置了一個轉(zhuǎn)換成樹的閾值,所以它比hashtable要復(fù)雜一些
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        // 元素?cái)?shù)量達(dá)到閾值開始擴(kuò)容
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

resize方法:

final Node<K,V>[] resize() {
        Node<K,V>[] oldTab = table;
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        int oldThr = threshold;
        int newCap, newThr = 0;
        if (oldCap > 0) {
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold
        }
        else if (oldThr > 0) // initial capacity was placed in threshold
            newCap = oldThr;
        else {               // zero initial threshold signifies using defaults
            newCap = DEFAULT_INITIAL_CAPACITY;
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
        if (newThr == 0) {
            float ft = (float)newCap * loadFactor;
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
        threshold = newThr;
        @SuppressWarnings({"rawtypes","unchecked"})
            Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        table = newTab;
        if (oldTab != null) {
            for (int j = 0; j < oldCap; ++j) {
                Node<K,V> e;
                if ((e = oldTab[j]) != null) {
                    oldTab[j] = null;
                    if (e.next == null)
                        newTab[e.hash & (newCap - 1)] = e;
                    else if (e instanceof TreeNode)
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    else { // preserve order
                        Node<K,V> loHead = null, loTail = null;
                        Node<K,V> hiHead = null, hiTail = null;
                        Node<K,V> next;
                        do {
                            next = e.next;
                            if ((e.hash & oldCap) == 0) {
                                if (loTail == null)
                                    loHead = e;
                                else
                                    loTail.next = e;
                                loTail = e;
                            }
                            else {
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
                        if (loTail != null) {
                            loTail.next = null;
                            newTab[j] = loHead;
                        }
                        if (hiTail != null) {
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • To Solve: 什么時(shí)候會使用HashMap?有什么特點(diǎn)? HashMap的工作原理? get和put的原理?...
    糜糜糜糜人閱讀 377評論 2 3
  • 上篇文章我們通過部分源碼和結(jié)構(gòu)圖了解了HashMap的數(shù)據(jù)結(jié)構(gòu),感興趣的小伙伴看這里HashMap實(shí)現(xiàn)原理基礎(chǔ)篇。...
    Shmily魚閱讀 2,182評論 0 5
  • 目的:java中為實(shí)現(xiàn)能夠從一個數(shù)量龐大的容器中取出某一個容器(快速查找),做了這個容器Map,而HashMap是...
    4ea0af17fd67閱讀 207評論 0 1
  • 漫長的比賽結(jié)束了,帶了兩周的訓(xùn)練,很幸苦,前幾天都需要一兩個小時(shí)來備課。第四名。
    songdear閱讀 101評論 0 0
  • 上午九點(diǎn)多來到這,也是很冷的。 貨車大吊車太高,進(jìn)不來。又找了個叉車把壓濾機(jī)挑了過去,才裝上車,相當(dāng)費(fèi)勁。 中午在...
    巳哖閱讀 120評論 0 0