大战熟女丰满人妻av-荡女精品导航-岛国aaaa级午夜福利片-岛国av动作片在线观看-岛国av无码免费无禁网站-岛国大片激情做爰视频

專注Java教育14年 全國(guó)咨詢/投訴熱線:400-8080-105
動(dòng)力節(jié)點(diǎn)LOGO圖
始于2009,口口相傳的Java黃埔軍校
首頁 hot資訊 HashMap的底層實(shí)現(xiàn)原理

HashMap的底層實(shí)現(xiàn)原理

更新時(shí)間:2022-01-05 10:51:41 來源:動(dòng)力節(jié)點(diǎn) 瀏覽780次

1.HashMap底層實(shí)現(xiàn)原理

jdk7中HashMap的實(shí)現(xiàn)原理

HashMap map = new HashMap()

實(shí)例化后,底層創(chuàng)建一個(gè)長(zhǎng)度為16的一維數(shù)組Entry[]表。

map.put(key1,value1)

首先調(diào)用key1所在類的hashCode()計(jì)算key1的hash值。經(jīng)過一些算法計(jì)算,哈希值存儲(chǔ)在Entry數(shù)組中。

如果該位置的數(shù)據(jù)為空,則表示key1-value1添加成功。

如果這個(gè)位置的數(shù)據(jù)不為空,(表示這個(gè)位置有一個(gè)或多個(gè)數(shù)據(jù)(以鏈表的形式)),比較key1和一個(gè)或多個(gè)現(xiàn)有數(shù)據(jù)的hash值:

如果key1的hash值與現(xiàn)有數(shù)據(jù)的hash值不同,則key1-value1添加成功。

如果key1的hash值與已有數(shù)據(jù)(key2-value2)的hash值相同,則繼續(xù)比較:調(diào)用key1所在類的equals(key2)方法。如果equals()返回false,則key1-value1添加成功。如果equals() 返回true,則將Value2 替換為value1。

如果添加成功,key1-value1 和原始數(shù)據(jù)存儲(chǔ)在一個(gè)鏈表中。

在不斷增加的過程中,會(huì)涉及到產(chǎn)能擴(kuò)張的問題。當(dāng)超過臨界值(且要存儲(chǔ)的位置不為空)時(shí),將擴(kuò)大容量。默認(rèn)的擴(kuò)容方式是將容量擴(kuò)容到原來容量的兩倍,然后復(fù)制原來的數(shù)據(jù)。

與jdk7相比,jdk8中HashMap的實(shí)現(xiàn)是不同的

實(shí)例化new HashMap()時(shí),底層不會(huì)創(chuàng)建長(zhǎng)度為16的數(shù)組。

jdk 8的底層數(shù)組是Node,不是Entry。

第一次調(diào)用 put() 方法時(shí),底層會(huì)創(chuàng)建一個(gè)長(zhǎng)度為 16 的數(shù)組。

jdk7的底層結(jié)構(gòu)只是數(shù)組+列表。jdk8的底層結(jié)構(gòu)是數(shù)組+鏈表+紅黑樹。當(dāng)鏈表形式的數(shù)組的某個(gè)索引位置的元素個(gè)數(shù)大于8,且當(dāng)前數(shù)組的長(zhǎng)度大于64時(shí),該索引位置的數(shù)據(jù)改為存儲(chǔ)在紅黑樹中。

2.HashMap的源碼分析

jdk7中HashMap的源碼:

實(shí)例化一個(gè)HashMap時(shí),空參數(shù)構(gòu)造函數(shù)如下。默認(rèn)情況下,HashMap 的容量為 16,負(fù)載因子為 0.75

    /**
     * Constructs an empty <tt>HashMap</tt> with the default initial capacity
     * (16) and the default load factor (0.75).
     */
    public HashMap() {
        this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
    }

進(jìn)一步調(diào)用帶參數(shù)的構(gòu)造函數(shù),最大容量_容量為2^30,如果傳輸?shù)娜萘啃∮?6,則向左移動(dòng)增加到16。閾值是指擴(kuò)展的臨界值。超過臨界值時(shí)應(yīng)開始膨脹。閾值 = 容量 * 填充因子。然后創(chuàng)建Entry數(shù)組 

    /**
     * Constructs an empty <tt>HashMap</tt> with the specified initial
     * capacity and load factor.
     *
     * @param  initialCapacity the initial capacity
     * @param  loadFactor      the load factor
     * @throws IllegalArgumentException if the initial capacity is negative
     *         or the load factor is nonpositive
     */
    public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
        // Find a power of 2 >= initialCapacity
        int capacity = 1;
        while (capacity < initialCapacity)
            capacity <<= 1;
        this.loadFactor = loadFactor;
        threshold = (int)Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
        table = new Entry[capacity];
        useAltHashing = sun.misc.VM.isBooted() &&
                (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
        init();
    }

添加元素調(diào)用put()方法,源碼如下

當(dāng)key為null時(shí),HashMap處理單獨(dú)返回putForNullKey(value),然后計(jì)算key的hash值。它通過哈希值和數(shù)組的長(zhǎng)度來計(jì)算數(shù)組的位置。如果該位置沒有值,則直接存儲(chǔ)。如果位置有值,就會(huì)判斷hash值是equal()還是key值。如果相同,則將舊數(shù)據(jù)替換為新數(shù)據(jù)。

    public V put(K key, V value) { 
        if (key == null) 
            return putForNullKey(value); 
        int hash = hash(key); 
        int i = indexFor(hash, table.length); 
        for (Entry<K,V> e = table[i]; e != null; e = e.next) { 
            Object k; 
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { 
                V oldValue = e.value; 
                e.value = 值;
                e.recordAccess(this); 
                返回舊值;
            } 
        } 
        modCount的++; 
        addEntry(hash, key, value, i); 
        返回空;
    } 
    / **
     * 將具有指定鍵、值和哈希碼的新條目添加到
     指定的存儲(chǔ)桶中。
     如果合適,此* 方法負(fù)責(zé)調(diào)整表的大小。
     * 
     * 子類覆蓋它以改變 put 方法的行為。
     */ 
    void addEntry(int hash, K key, V value, int bucketIndex) { 
        if ((size >= threshold) && (null != table[bucketIndex])) { 
            resize(2 * table.length); 
            hash = (null != key) ? 哈希(鍵):0;
            bucketIndex = indexFor(hash, table.length); 
        } 
        createEntry(hash, key, value, bucketIndex); 
    } 
    / **
     * 與 addEntry 類似,只是在創(chuàng)建條目時(shí)使用此版本
     * 作為 Map 構(gòu)造或“偽構(gòu)造”(克隆、
     * 反序列化)的一部分。此版本無需擔(dān)心調(diào)整表格大小。
     * 
     * 子類覆蓋它以改變HashMap(Map)、
     * clone 和readObject 的行為。
     */ 
    void createEntry(int hash, K key, V value, int bucketIndex) { 
        Entry<K,V> e = table[bucketIndex]; 
        table[bucketIndex] = new Entry<>(hash, key, value, e); 
        尺寸++;
    }

jdk8中HashMap的源碼:

實(shí)例化HashMap時(shí),空參數(shù)構(gòu)造函數(shù)如下。默認(rèn)加載因子為0.75,但沒有指定默認(rèn)長(zhǎng)度,即不創(chuàng)建長(zhǎng)度為16的數(shù)組。

    /** 
     * 構(gòu)造一個(gè)空的<tt>HashMap</tt>,默認(rèn)初始容量
     *(16)和默認(rèn)負(fù)載因子(0.75)。
     */ 
    public HashMap() { 
        this.loadFactor = DEFAULT_LOAD_FACTOR; // 所有其他字段默認(rèn)
    }

jdk 8的底層數(shù)組是Node,不是Entry

    /**
     * The table, initialized on first use, and resized as
     * necessary. When allocated, length is always a power of two.
     * (We also tolerate length zero in some operations to allow
     * bootstrapping mechanics that are currently not needed.)
     */
    transient Node<K,V>[] table;

put() 方法進(jìn)一步調(diào)用 putVal() 方法。第一次調(diào)用元素時(shí),會(huì)調(diào)用reset()方法來擴(kuò)展容量。數(shù)組中的存儲(chǔ)位置是通過 和 運(yùn)算計(jì)算出來的。如果位置沒有值,則直接存儲(chǔ)在元素中。如果位置已經(jīng)有值,首先判斷hash值是否相等。如果哈希值相等且鍵值或相等也相等,則替換它們。如果hash值相等,key值和equals不相等,進(jìn)入for循環(huán),遍歷鏈表中的所有值,看是否不等于所有值的key和equals。如果存儲(chǔ)的值相同,則直接跳出循環(huán)并替換它們。當(dāng)一個(gè)新值被放入鏈表時(shí),我們需要判斷 tree if_ threshold 的值(默認(rèn)為 8)。如果鏈表的長(zhǎng)度大于默認(rèn)值,則調(diào)用 treeifyBin() 方法將其轉(zhuǎn)換為紅黑樹。轉(zhuǎn)換為紅黑樹時(shí),如果此時(shí)數(shù)組為空或數(shù)組長(zhǎng)度小于MIN_TREEIFY_CAPACITY(樹狀化時(shí)最小哈希表容量,默認(rèn)為64),則應(yīng)進(jìn)行reset擴(kuò)展操作。這個(gè) min_ TREEIFY_ 能力的值至少是樹 if_ 閾值的四倍。如果此時(shí)數(shù)組為空或數(shù)組長(zhǎng)度小于MIN_TREEIFY_CAPACITY(節(jié)點(diǎn)樹狀化時(shí)的最小哈希表容量,默認(rèn)為64),則應(yīng)執(zhí)行重置擴(kuò)展操作。這個(gè) min_ TREEIFY_ 能力的值至少是樹 if_ 閾值的四倍。如果此時(shí)數(shù)組為空或數(shù)組長(zhǎng)度小于MIN_TREEIFY_CAPACITY(節(jié)點(diǎn)樹狀化時(shí)的最小哈希表容量,默認(rèn)為64),則應(yīng)執(zhí)行重置擴(kuò)展操作。這個(gè) min_ TREEIFY_ 能力的值至少是樹 if_ 閾值的四倍。

    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }
     /**
     * Implements Map.put and related methods
     *
     * @param hash hash for key
     * @param key the key
     * @param value the value to put
     * @param onlyIfAbsent if true, don't change existing value
     * @param evict if false, the table is in creation mode.
     * @return previous value, or null if none
     */
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        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);
                        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;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }
    /**
     * Initializes or doubles table size.  If null, allocates in
     * accord with initial capacity target held in field threshold.
     * Otherwise, because we are using power-of-two expansion, the
     * elements from each bin must either stay at same index, or move
     * with a power of two offset in the new table.
     *
     * @return the table
     */
    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;
    }

大家如果想了解更多相關(guān)知識(shí),可以關(guān)注一下動(dòng)力節(jié)點(diǎn)的Java基礎(chǔ)教程,里面的課程內(nèi)容豐富,比較適合沒有基礎(chǔ)的小伙伴學(xué)習(xí),希望對(duì)大家能夠有所幫助。

提交申請(qǐng)后,顧問老師會(huì)電話與您溝通安排學(xué)習(xí)

  • 全國(guó)校區(qū) 2025-10-10 搶座中
免費(fèi)課程推薦 >>
技術(shù)文檔推薦 >>
主站蜘蛛池模板: 网络毛片 | 国产成人久久精品激情 | 琪琪色在线视频 | 亚洲国产欧美国产综合一区 | 波多野野结衣1区二区 | 国产欧美另类久久精品91 | 99久久精品一区二区三区 | 天天爽天天 | 色网址在线观看 | 夜夜嗨影院 | 国产亚洲精品久久yy5099 | 一区二区三区在线 | 欧 | 99综合 | 欧美精品一区二区三区视频 | 天天操天天干天天爱 | 国产精品福利尤物youwu | 91正在播放极品白嫩在线观看 | 国产色婷婷精品综合在线手机播放 | 毛片一级免费 | 亚洲成a | 国产精品久久久久无毒 | 国产a v高清一区二区三区 | 日韩精品一区二区三区免费观看 | 精品久久洲久久久久护士免费 | 欧美日一级片 | 一级香蕉免费毛片 | 久久久人体| 亚洲福利精品一区二区三区 | 狠狠色狠狠色综合网 | 欧美jizz40性欧美 | 亚洲 欧美 另类中文字幕 | 韩国女主播一区二区三区视频 | 久草在线视频首页 | 精品福利在线视频 | 全免费一级毛片在线播放 | 日韩毛片在线免费观看 | 日本黄页网站在线观看 | 久草美女 | 国产精品99久久久久久宅男 | 久久不见久久见免费影院 | 久久99这里只有精品国产 |