成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

阿里面試:HashMap是如何實現擴容?哪三步?

開發 前端
為了盡可能地減少擴容操作的次數,通常會將負載因子設置為一個較小的值,例如0.75,以保證哈希表的容量能夠滿足存儲需求,同時又不會造成太多的空間浪費。

1.達到閾值開始擴容

如下圖所示:

圖片圖片

HashMap的擴容機制是當HashMap中的元素個數超過了負載因子(loadFactor)與初始容量(initialCapacity)的乘積時,就會觸發擴容機制。

默認的構造函數指定了擴容因子:0.75, 默認容量是16,如下所示:

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // 相當于16
static final float DEFAULT_LOAD_FACTOR = 0.75f;


public HashMap(int initialCapacity) {
    this(initialCapacity, DEFAULT_LOAD_FACTOR);
}

元素數量達到了閾值(即負載因子 * 桶的數量)時,會觸發擴容操作,也就是說第1次擴容的動作會在元素個數達到12(16*0.75)的時候觸發擴容。

為了盡可能地減少擴容操作的次數,通常會將負載因子設置為一個較小的值,例如0.75,以保證哈希表的容量能夠滿足存儲需求,同時又不會造成太多的空間浪費。

2.開始擴容執行

HashMap通過resize()方法進行擴容,容量規則為2的冪次。

如下所示:

final Node<K,V>[] resize() {
    Node<K,V>[] oldTab = table;
    int oldCap = (oldTab == null) ? 0 : oldTab.length;
    int oldThr = threshold;
    int newCap, newThr = 0;
    //以前的容量大于0,也就是hashMap中已經有元素了,或者new對象的時候設置了初始容量
    if (oldCap > 0) {
        //如果以前的容量大于限制的最大容量1<<30,則設置臨界值為int的最大值2^31-1
        if (oldCap >= MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return oldTab;
        }
        /**
         * 如果以前容量的2倍小于限制的最大容量,同時大于或等于默認的容量16,則設置臨界值為以前臨界值的2
         * 倍,因為threshold = loadFactor*capacity,capacity擴大了2倍,loadFactor不變,
         * threshold自然也擴大2倍。
         */
        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                 oldCap >= DEFAULT_INITIAL_CAPACITY)
            newThr = oldThr << 1; // double threshold
    }
    /**
     * 在HashMap構造器Hash(int initialCapacity, float loadFactor)中有一句代碼,this.threshold    
     * = tableSizeFor(initialCapacity), 表示在調用構造器時,默認是將初始容量暫時賦值給了
     * threshold臨界值,因此此處相當于將上一次的初始容量賦值給了新的容量。什么情況下會執行到這句?當調用    
     * 了HashMap(int initialCapacity)構造器,還沒有添加元素時
     */
    else if (oldThr > 0) 
        newCap = oldThr;
    /**
     * 調用了默認構造器,初始容量沒有設置,因此使用默認容量DEFAULT_INITIAL_CAPACITY(16),臨界值
     * 就是16*0.75
     */
    else {               
        newCap = DEFAULT_INITIAL_CAPACITY;
        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
    }
    //對臨界值做判斷,確保其不為0,因為在上面第二種情況(oldThr > 0),并沒有計算newThr
    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
    table = newTab;
    if (oldTab != null) {
        //遍歷將原來table中的數據放到擴容后的新表中來
        for (int j = 0; j < oldCap; ++j) {
            Node<K,V> e;
            if ((e = oldTab[j]) != null) {
                oldTab[j] = null;
                //沒有鏈表Node節點,直接放到新的table中下標為【e.hash & (newCap - 1)】位置即可
                if (e.next == null)
                    newTab[e.hash & (newCap - 1)] = e;
                //如果是treeNode節點,則樹上的節點放到newTab中
                else if (e instanceof TreeNode)
                    ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                //如果e后面還有鏈表節點,則遍歷e所在的鏈表,
                else { // 保證順序
                    Node<K,V> loHead = null, loTail = null;
                    Node<K,V> hiHead = null, hiTail = null;
                    Node<K,V> next;
                    do {
                        //記錄下一個節點
                        next = e.next;
                        /**
                         * newTab的容量是以前舊表容量的兩倍,因為數組table下標并不是根據循環逐步遞增
                         * 的,而是通過(table.length-1)& hash計算得到,因此擴容后,存放的位置就
                         * 可能發生變化,那么到底發生怎樣的變化呢,就是由下面的算法得到.
                         *
                         * 通過e.hash & oldCap來判斷節點位置通過再次hash算法后,是否會發生改變,如
                         * 果為0表示不會發生改變,如果為1表示會發生改變。到底怎么理解呢,舉個例子:
                         * e.hash = 13 二進制:0000 1101
                         * oldCap = 32 二進制:0001 0000
                         *  &運算:0  二進制:0000 0000
                         * 結論:元素位置在擴容后不會發生改變
                         */
                        if ((e.hash & oldCap) == 0) {
                            if (loTail == null)
                                loHead = e;
                            else
                                loTail.next = e;
                            loTail = e;
                        }
                        /**
                         * e.hash = 18 二進制:0001 0010
                         * oldCap = 32 二進制:0001 0000
                         * &運算:32 二進制:0001 0000
                         * 結論:元素位置在擴容后會發生改變,那么如何改變呢?
                         * newCap = 64 二進制:0010 0000
                         * 通過(newCap-1)&hash
                         * 即0001 1111 & 0001 0010 得0001 0010,32+2 = 34
                         */
                        else {
                            if (hiTail == null)
                                hiHead = e;
                            else
                                hiTail.next = e;
                            hiTail = e;
                        }
                    } while ((e = next) != null);
                    if (loTail != null) {
                        loTail.next = null;
                        /**
                         * 若(e.hash & oldCap) == 0,下標不變,將原表某個下標的元素放到擴容表同樣
                         * 下標的位置上
                         */
                        newTab[j] = loHead;
                    }
                    if (hiTail != null) {
                        hiTail.next = null;
                        /**
                         * 若(e.hash & oldCap) != 0,將原表某個下標的元素放到擴容表中
                         * [下標+增加的擴容量]的位置上
                         */
                        newTab[j + oldCap] = hiHead;
                    }
                }
            }
        }
    }
    return newTab;
}

擴容會將HashMap的容量(即桶的數量)翻倍,擴容的大小是原來的2倍,并重新計算每個元素在新桶中的位置。

3.新建哈希表存儲擴容

擴容操作需要創建一個新的哈希表,并將舊哈希表中的元素重新分配到新哈希表的桶中。

重新分配元素時,HashMap會對每個元素的哈希值取模得到一個新的桶位置,并將元素插入到新的桶中。

擴容操作完成后,HashMap將使用新的哈希表來存儲元素,并釋放舊哈希表的內存空間。

責任編輯:武曉燕 來源: mikechen的互聯網架構
相關推薦

2021-09-27 07:11:18

MySQLACID特性

2024-04-28 08:52:33

RabbitMQ延遲隊列延遲插件

2010-04-20 20:53:35

實現網絡負載均衡

2010-02-26 16:16:15

2010-08-12 10:10:37

FlexMapABC

2009-11-10 12:55:26

VB.NET三維模型

2021-03-02 07:02:45

Linux操作系統

2009-11-16 13:04:04

PHP上傳文件代碼

2010-05-17 09:49:19

DataCore虛擬化

2024-07-05 17:47:21

@Async項目啟動類

2018-05-10 22:40:44

物聯網信息化傳統企業

2011-07-13 09:54:22

VMware故障vSphere

2009-02-04 09:45:05

Java SocketSocket APIJava編程

2020-11-04 00:00:29

Kerberos協議身份

2013-11-01 10:09:14

Windows 8.1升級

2021-05-06 11:06:52

人工智能語音識別聲聞檢索

2024-06-28 07:42:18

2012-08-08 17:05:36

App運營

2015-06-09 09:25:34

2010-05-20 11:12:13

SVN插件安裝
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 欧美亚洲免费 | 精品成人av | 99久久日韩精品免费热麻豆美女 | 国产精品视频一区二区三 | 丁香久久| 亚洲精品影院 | 高清久久久 | 91在线观看免费 | 欧美精品区 | 综合久久久久 | jlzzjlzz国产精品久久 | 国产一区二区三区四区区 | 日韩精品一区二区在线观看 | 六月婷婷久久 | 精品视频一区二区 | 国际精品鲁一鲁一区二区小说 | a免费在线| 亚洲欧美国产精品久久 | 欧美日韩精品中文字幕 | 欧美激情精品久久久久久变态 | 日韩在线看片 | 精品国产一区二区三区在线观看 | 国产高清在线视频 | 91久久国产综合久久 | 精品国产欧美一区二区三区成人 | 精品视频在线一区 | 亚洲精品一区中文字幕乱码 | 欧美视频精品 | 日本特黄a级高清免费大片 国产精品久久性 | 久久久久久成人 | 亚洲一区中文字幕 | 视频1区| 亚洲精品福利视频 | 青青草华人在线视频 | 91色在线 | 日韩av看片| 一区二区三区四区毛片 | 一级看片免费视频 | 91久久 | 国产高清免费 | 亚洲激情在线观看 |