當前位置:成語大全網 - 書法字典 - Goland map基本原理

Goland map基本原理

Map是Go語言中的基本數據結構,在日常使用中經常用到。但是底層是如何實現的呢?

壹般來說,golang的map是hashmap,以數組+鏈表的形式實現,通過zipper方法消除哈希沖突。

Golang的地圖由兩個重要的結構組成,hmap和bmap(在下面解釋)。主要的壹個是hmap包含壹個指向bmap數組的指針,key在hash函數後得到壹個數字。該數字的低位用於選擇bmap(如下表的bmap數組指針),高位用於將其放入bmap的【8】uint 8數組中進行快速試錯。然後壹個bmap可以指向下壹個bmap(拉鏈)。

Golang中map的底層實現是壹個哈希表,因此實現map的過程實際上就是實現哈希表的過程。在這個哈希表中,有兩種主要結構,壹種稱為hmap(Go map的頭),另壹種稱為bmap(Go map的桶,通常稱為bucket)。這兩種結構如下所示:

hmap:

圖中有許多字段,但很容易理解地圖的架構。您只需要關心壹個用紅色標記的字段:桶數組。Golang地圖中用於存儲的結構是桶形數組。bucket(bmap)的結構是什麽?

桶:

與hmap相比,bucket的結構更簡單,用紅色標記的字段仍然是“核心”,我們使用的map中的鍵和值都存儲在這裏。“高哈希值”數組記錄了與當前桶中的鍵相關的“索引”,稍後將詳細介紹。另壹個字段是指向擴展桶的指針,它使桶形成壹個鏈表結構。例如,如下圖:

可以看出hmap和bucket之間的關系是這樣的:

Bucket是壹個鏈表,所以整體結構應該是這樣的:

哈希表的特點是會有壹個哈希函數,它對妳發送的密鑰進行哈希運算得到壹個唯壹的值,這個值通常是壹個數值。Golang的地圖也有這樣壹個哈希函數,它也將計算壹個唯壹的值。Golang也非常有趣地使用了這個值。

Golang根據目的將獲得的值分為兩部分:高位和低位。

如圖所示,藍色為高,紅色為低。然後低位用於查找當前鍵屬於hmap中的哪個桶,高位用於查找桶中的哪個鍵。如上所述,bucket中有壹個屬性字段是“高哈希值”數組,藍色的高值存儲在該數組中,用於聲明當前bucket中有哪些“鍵”,這便於搜索。需要指出的是,我們的映射中的鍵/值都存儲在同壹個數組中。數組中的順序如下:

它不是鍵0/值0/鍵1/值1的形式。這樣做的好處是,當鍵和值的長度不同時,可以消除填充造成的空間浪費。

現在,我們可以得到Go語言圖的整個結構圖:(哈希結果的低階部分用於選擇將KV放在bmap數組中的哪個bmap,高階部分用於快速預覽密鑰和快速試錯)。

地圖的擴展

當上述哈希表增長時,Go語言會將桶數組的數量增加壹倍,生成壹個新的桶數組,並將舊數組的數據遷移到新數組中。

裝載系數

判斷擴展的條件是哈希表中的loadFactor。

加載因子是壹個閾值,通常表示為:哈希中包含的元素數除以位置總數。它是“沖突機會”和“空間利用率”之間的平衡和妥協:裝載系數越小,空間空置率越高,空間利用率越低,但裝載系數越大,空間利用率越高,但“沖突機會”也越高。

每個哈希表都有壹個加載因子,如果值超過加載因子,哈希表將被擴展。

Golang映射的負載因子的公式為:映射length/2^B(這是bmap數組的長度,b是低階位數),閾值為6.5。其中b可以理解為容量擴展的次數。

當Go的映射長度增長到大於加載因子所需的映射長度時,Go語言將生成壹個新的存儲桶數組,然後將舊存儲桶數組移動到屬性字段oldbucket。註意:舊數組中的元素不會立即轉義到新存儲桶中,但是只有在訪問特定存儲桶時,存儲桶中的數據才會轉移到新存儲桶中。

如下圖所示:擴容時,舊數據和新生成的數組將保存在Go的map結構中。

上半部分表示帶有數據的舊桶,下半部分表示新生成的新桶。藍色代表有數據的桶,橙色代表空桶。

Map在擴展時不會立即遷移新數據,而是在訪問舊存儲桶的數據時遷移舊數據,如下圖所示:

註意:舊的bucket不會被直接刪除,但是原始的引用將被刪除,內存將被GC清除。

刪除地圖中的數據

如果您了解map的整體結構,那麽查找、更新和刪除的基本步驟應該很清楚。這裏就不贅述了。

值得註意的是,在map中找到數據後,分別對key和值進行如下操作:

1

2

1.如果“key”是指針類型,就將其留空並等待GC將其清除;

2.如果是值類型,則清除相關內存。

3.以同樣的方式,對“值”進行同樣的操作。

4.最後,將key高值對應的數組索引設置為空。