日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

深度!HashMap的底層數據結構

來源: 責編: 時間:2023-09-18 21:41:31 338觀看
導讀一、HashMap基礎機構HashMap 由數組和鏈表(或紅黑樹)組成。數組是 HashMap 的主體,鏈表和紅黑樹則是為了解決哈希沖突而存在的。數組中的每個元素都是一個單向鏈表的頭結點,每個鏈表都是由若干個 Node 節點組成的,每個節點

TEc28資訊網——每日最新資訊28at.com

一、HashMap基礎機構

HashMap 由數組和鏈表(或紅黑樹)組成。數組是 HashMap 的主體,鏈表和紅黑樹則是為了解決哈希沖突而存在的。數組中的每個元素都是一個單向鏈表的頭結點,每個鏈表都是由若干個 Node 節點組成的,每個節點都包含了鍵值對的信息,以及指向下一個節點的指針。當多個鍵映射到同一個位置時,它們會被存儲在同一個鏈表中(或者是同一個紅黑樹中)。當鏈表長度超過閾值(默認為 8)時,鏈表就會被轉換成紅黑樹,這樣可以提高查找效率。TEc28資訊網——每日最新資訊28at.com

在 JDK1.8 中,HashMap 還引入了一個新的概念,叫做負載因子(load factor),它是指哈希表中鍵值對的數量與數組長度的比值。當鍵值對的數量超過了負載因子與數組長度的乘積時,就會觸發擴容操作,HashMap 會自動將數組長度擴大一倍,并將原來的鍵值對重新分配到新的數組中。這樣做的目的是為了保證散列表的性能,因為當負載因子過高時,散列表的性能會急劇下降。TEc28資訊網——每日最新資訊28at.com

二、HashMap的底層數據結構

解答:在jdk1.8以前,HashMa采用鏈表+數組,自Jdk1.8以后,HashMap采用鏈表+數組+紅黑樹。在下圖中橫鏈(0-15)表中表示數組,豎(1-8)表示鏈表,在數組長度超過8之后,hashmap將數組自動轉為紅黑樹。TEc28資訊網——每日最新資訊28at.com

TEc28資訊網——每日最新資訊28at.com

HashMapJDK1.8鏈表和紅黑樹轉化TEc28資訊網——每日最新資訊28at.com

三、JDK1.8對hash算法和尋址算法如何優化的?

1、對Hash值算法的優化

static final int hash(Object key) {        int h;        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);    }

有一個key的Hash_1值:TEc28資訊網——每日最新資訊28at.com

Hash_1: 1111 1111 1111 1111 1111 1010 0111 1100
h >>> 16 // 表示對該hash值右移16位

右移后的結果Hash_2為:TEc28資訊網——每日最新資訊28at.com

Hash_2: 0000 0000 0000 0000 1111 1111 1111 1111

對上述Hash_1和Hash_2的兩個值進行異或TEc28資訊網——每日最新資訊28at.com

Hash_1: 1111 1111 1111 1111 1111 1010 0111 1100Hash_2: 0000 0000 0000 0000 1111 1111 1111 1111=====>: 1111 1111 1111 1111 0000 0101 1000 0011 =====> 轉為10進制int值,這個值就是這個key的hash值

hash算法的優化:對每個hash值,在它的低16位中,讓高低16位進行異或,讓它的低16位同時保持了高低16位的特征,盡量避免一些hash值后續出現沖突,大家可能會進入數組的同一位置。TEc28資訊網——每日最新資訊28at.com

2、對尋址算法的優化

TEc28資訊網——每日最新資訊28at.com

(p = tab[i = (n - 1) & hash]   // (n-1) & hash ==> 數組里的一個位置

hash & (n-1) 效果是跟hash對n取模是一樣的,但是與運算的性能要比hash對n取模要高很多。數組的長度會一直是2的n次方,只要他保持數組長度是2的n次方。TEc28資訊網——每日最新資訊28at.com

  • 尋址為什么不用取模?

對于上面尋址算法,由于計算機對比取模,與運算會更快。所以為了效率,HashMap 中規定了哈希表長度為 2 的 k 次方,而 2^k-1 轉為二進制就是 k 個連續的 1,那么 hash & (k 個連續的 1) 返回的就是 hash 的低 k 個位,該計算結果范圍剛好就是 0 到 2^k-1,即 0 到 length - 1,跟取模結果一樣。TEc28資訊網——每日最新資訊28at.com

也就是說,哈希表長度 length 為 2 的整次冪時, hash & (length - 1) 的計算結果跟 hash % length 一樣,而且效率還更好。TEc28資訊網——每日最新資訊28at.com

  • 為什么不直接用 hashCode() 而是用它的高 16 位進行異或計算新 hash 值?#

int 類型占 32 位,可以表示 2^32 種數(范圍:-2^31 到 2^31-1),而哈希表長度一般不大,在 HashMap 中哈希表的初始化長度是 16(HashMap 中的 DEFAULT_INITIAL_CAPACITY),如果直接用 hashCode 來尋址,那么相當于只有低 4 位有效,其他高位不會有影響。這樣假如幾個 hashCode 分別是 210、220、2^30,那么尋址結果 index 就會一樣而發生沖突,所以哈希表就不均勻分布了。TEc28資訊網——每日最新資訊28at.com

尋址算法的優化:用與運算替代取模,提升性能。(由于計算機對比取模,與運算會更快)TEc28資訊網——每日最新資訊28at.com

四、HashMap是如何解決hash碰撞問題

hash沖突問題,鏈表+紅黑樹,O(n)和O(logN)。TEc28資訊網——每日最新資訊28at.com

hashmap采用的就是鏈地址法(拉鏈法),jdk1.7中,當沖突時,在沖突的地址上生成一個鏈表,將沖突的元素的key,通過equals進行比較,相同即覆蓋,不同則添加到鏈表上,此時如果鏈表過長,效率就會大大降低,查找和添加操作的時間復雜度都為O(n);但是在jdk1.8中如果鏈表長度大于8,鏈表就會轉化為紅黑樹,時間復雜度也降為了O(logn),性能得到了很大的優化。TEc28資訊網——每日最新資訊28at.com

TEc28資訊網——每日最新資訊28at.com

HashMapJDK1.8鏈表和紅黑樹轉化TEc28資訊網——每日最新資訊28at.com

TEc28資訊網——每日最新資訊28at.com

五、HashMap是如何進行擴容的

HashMap底層是一個數組,當這個數組滿了之后,他就會自動進行擴容,變成一個更大數組。TEc28資訊網——每日最新資訊28at.com

1、JDK1.7下的擴容機制

void resize(int newCapacity) {        Entry[] oldTable = table;        int oldCapacity = oldTable.length;        if (oldCapacity == MAXIMUM_CAPACITY) {            threshold = Integer.MAX_VALUE;            return;        }         Entry[] newTable = new Entry[newCapacity];        transfer(newTable, initHashSeedAsNeeded(newCapacity));        table = newTable;        threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);    }

代碼中可以看到,如果原有table長度已經達到了上限,就不再擴容了。如果還未達到上限,則創建一個新的table,并調用transfer方法:TEc28資訊網——每日最新資訊28at.com

/**     * Transfers all entries from current table to newTable.     */    void transfer(Entry[] newTable, boolean rehash) {        int newCapacity = newTable.length;        for (Entry<K,V> e : table) {            while(null != e) {                Entry<K,V> next = e.next;              //注釋1                if (rehash) {                    e.hash = null == e.key ? 0 : hash(e.key);                }                int i = indexFor(e.hash, newCapacity); //注釋2                e.next = newTable[i];                  //注釋3                newTable[i] = e;                       //注釋4                e = next;                              //注釋5            }        }    }

transfer方法的作用是把原table的Node放到新的table中,使用的是頭插法,也就是說,新table中鏈表的順序和舊列表中是相反的,在HashMap線程不安全的情況下,這種頭插法可能會導致環狀節點。TEc28資訊網——每日最新資訊28at.com

2、JDK1.8下的擴容機制

源碼如下:TEc28資訊網——每日最新資訊28at.com

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 // 重新計算TREEIFY_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) {  // 重新計算原來鏈表中的值的hash值在新表對應的hash值            for (int j = 0; j < oldCap; ++j) {                Node<K,V> e;                if ((e = oldTab[j]) != null) {                    oldTab[j] = null;                    if (e.next == null)  // 如果元素e的下一個位置沒有值,則說明可以存放元素                        newTab[e.hash & (newCap - 1)] = e;                     else if (e instanceof TreeNode) // 如果已經是紅黑樹的節點,那就對其重新劃分                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);                    else { // preserve order                        // loHead: 下標不變情況下的鏈表頭                        // loTail: 下標不變情況下的鏈表尾                        // hiHead: 下標改變情況下的鏈表頭                        // hiTail: 下標改變情況下的鏈表尾                        // 如果                        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) { // 元素e的最新hash如果與原來的值與計算之后如果值為0,就說明是使用原來的index                                // 尾插法插入元素e                                if (loTail == null)                                    loHead = e;                                else                                    loTail.next = e;                                loTail = e;                            }                            else {                                // 與運算不等于0則說明使用新的index                                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;    }

正常情況下,計算節點在table中的下標的方法是:hash&(oldTable.length-1),擴容之后,table長度翻倍,計算table下標的方法是hash&(newTable.length-1),也就是hash&(oldTable.length*2-1),于是我們有了這樣的結論:這新舊兩次計算下標的結果,要不然就相同,要不然就是新下標等于舊下標加上舊數組的長度。TEc28資訊網——每日最新資訊28at.com

數組長度為16時,有兩個keyA和keyB。TEc28資訊網——每日最新資訊28at.com

KeyA:n-1:   0000 0000 0000 0000 0000 0000 0000 1111hash1: 1111 1111 1111 1111 0000 1111 0000 0101&結果:  0000 0000 0000 0000 0000 0000 0000 0101 = 5KeyB:n-1:   0000 0000 0000 0000 0000 0000 0000 1111 hash1: 1111 1111 1111 1111 0000 1111 0001 0101&結果:  0000 0000 0000 0000 0000 0000 0000 0101 = 5

在數組長度為16的時候,他們兩個hash值沖突會使用拉鏈發解決沖突。TEc28資訊網——每日最新資訊28at.com

當數組長度擴容到32之后,需要重新對每個hash值進行尋址,也就是每個hash值跟新的數組length-1 進行操作。TEc28資訊網——每日最新資訊28at.com

KeyA:n-1:   0000 0000 0000 0000 0000 0000 000*1* 1111hash1: 1111 1111 1111 1111 0000 1111 0000 0101&結果:  0000 0000 0000 0000 0000 0000 0000 0101 = 5KeyB:n-1:   0000 0000 0000 0000 0000 000*1* 0000 1111 hash1: 1111 1111 1111 1111 0000 1111 0001 0101&結果:  0000 0000 0000 0000 0000 000*1* 0000 0101 = 21

判斷二進制結果是否多出一個bit的1,如果沒有多,那就用原來的index,如果多出來了那就用index+oldCap,通過這個方式,避免了rehash的時候,用每個hash對新數組的length取模,取模性能不高,位運算性能比較高。TEc28資訊網——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-10459-0.html深度!HashMap的底層數據結構

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: Spring Cloud Gateway提供的簡易網關實現方式,你使用過嗎?

下一篇: LLM構建AI應用 —— 工程師如何使用黑盒工具

標簽:
  • 熱門焦點
  • K60至尊版剛預熱 一加Ace2 Pro正面硬剛

    Redmi這邊剛如火如荼的宣傳了K60 Ultra的各種技術和硬件配置,作為競品的一加也坐不住了。一加中國區總裁李杰發布了兩條微博,表示在自家的一加Ace2上早就已經采用了和PixelWo
  • 6月安卓手機好評榜:魅族20 Pro蟬聯冠軍

    性能榜和性價比榜之后,我們來看最后的安卓手機好評榜,數據來源安兔兔評測,收集時間2023年6月1日至6月30日,僅限國內市場。第一名:魅族20 Pro好評率:95%5月份的時候魅族20 Pro就是
  • 5月iOS設備性能榜:M1 M2依舊是榜單前五

    和上個月一樣,沒有新品發布的iOS設備性能榜的上榜設備并沒有什么更替,僅僅只有跑分變化而產生的排名變動,剛剛開始的蘋果WWDC2023,推出的產品也依舊是新款Mac Pro、新款Mac Stu
  • 如何通過Python線程池實現異步編程?

    線程池的概念和基本原理線程池是一種并發處理機制,它可以在程序啟動時創建一組線程,并將它們置于等待任務的狀態。當任務到達時,線程池中的某個線程會被喚醒并執行任務,執行完任
  • 三分鐘白話RocketMQ系列—— 如何發送消息

    我們知道RocketMQ主要分為消息 生產、存儲(消息堆積)、消費 三大塊領域。那接下來,我們白話一下,RocketMQ是如何發送消息的,揭秘消息生產全過程。注意,如果白話中不小心提到相關代
  • 破圈是B站頭上的緊箍咒

    來源 | 光子星球撰文 | 吳坤諺編輯 | 吳先之每年的暑期檔都少不了瞄準追劇女孩們的古偶劇集,2021年有優酷的《山河令》,2022年有愛奇藝的《蒼蘭訣》,今年卻輪到小破站抓住了追
  • 郭明錤稱華為和江淮汽車合作開發問界MPV,定價100萬左右、計劃明年量產

    8 月 1 日消息,郭明錤今天在 Medium 平臺發布博文,稱華為正在和江淮汽車合作,開發售價在 100 萬元的問界 MPV,預計在 2024 年第 2 季度量產,銷量目標為
  • iQOO Neo8系列新品發布會

    旗艦雙芯 更強更Pro
  • Android 14發布:首批適配機型公布

    5月11日消息,谷歌在今天凌晨舉行了I/O大會,本次發布會谷歌帶來了自家的AI語言模型PaLM 2、谷歌Pixel Fold折疊屏、谷歌Pixel 7a手機,同時發布了Androi
Top 主站蜘蛛池模板: 宾川县| 巴里| 东乡县| 滦平县| 丰镇市| 竹溪县| 白水县| 博兴县| 措美县| 武邑县| 连城县| 寿光市| 东宁县| 东莞市| 博客| 讷河市| 萨迦县| 霍州市| 道真| 乌拉特中旗| 磐石市| 双柏县| 河北省| 威海市| 竹溪县| 湘潭市| 灵武市| 梅河口市| 房山区| 西乡县| 聂拉木县| 阿坝| 安多县| 三明市| 巴马| 图片| 台北县| 泰来县| 定远县| 乌拉特前旗| 海安县|