golang hashmap的使用及實(shí)現(xiàn)

基本語法

定義hashmap變量

由于go語言是一個(gè)強(qiáng)類型的語言瘪弓,因此hashmap也是有類型的,具體體現(xiàn)在key和value都必須指定類型禽最,比如聲明一個(gè)key為string,value也是string的map袱饭,
需要這樣做

var m map[string]string // 聲明一個(gè)hashmap川无,還不能直接使用,必須使用make來初始化
m = make(map[string]string) // 初始化一個(gè)map
m = make(map[string]string, 3) // 初始化一個(gè)map并附帶一個(gè)可選的初始bucket(非準(zhǔn)確值虑乖,只是有提示意義)

m := map[string]string{} // 聲明并初始化

m := make(map[string]string) // 使用make來初始化

大部分類型都能做key懦趋,某些類型是不能的,共同的特點(diǎn)是:不能使用==來比較疹味,包括: slice, map, function

get,set,delete

m := map[string]int
m["a"] = 1

fmt.Println(m["a"]) // 輸出 1

// 如果訪問一個(gè)不存在的key仅叫,返回類型默認(rèn)值
fmt.Println(m["b"]) // 輸出0

// 測試key是否存在
v, ok := m["b"]
if ok {
    ...
}

// 刪除一個(gè)key
delete(m, "a")

迭代器

// 只迭代key
for k := range m {
    ...
}

// 同時(shí)迭代key-value
for k, v := range m {
    ...
}

在迭代的過程中是可以對map進(jìn)行刪除和更新操作的,規(guī)則如下:

  • 迭代是無序的糙捺,跟插入是的順序無關(guān)
  • 迭代的過程中刪除一個(gè)key诫咱,無論遍歷還是沒有遍歷過都不會(huì)再遍歷到
  • 迭代的過程中添加一個(gè)key,不確定是否能遍歷到
  • 未初始化的map也可以迭代

其他

  • map的value是不可取地址的洪灯,意味著 &m["a"]這樣的語法是非法的
  • len可以獲取當(dāng)前map的kv個(gè)數(shù)

內(nèi)部結(jié)構(gòu)

hashmap結(jié)構(gòu)

golang的map是hash結(jié)構(gòu)的坎缭,意味著平均訪問時(shí)間是O(1)的。同傳統(tǒng)的hashmap一樣签钩,由一個(gè)個(gè)bucket組成:

hashmap內(nèi)部結(jié)構(gòu)
// A header for a Go map.
type hmap struct {
 // Note: the format of the Hmap is encoded in ../../cmd/internal/gc/reflect.go and
 // ../reflect/type.go.  Don't change this structure without also changing that code!
 count int // # live cells == size of map.  Must be first (used by len() builtin)
 flags uint8
 B     uint8  // log_2 of # of buckets (can hold up to loadFactor * 2^B items)
 hash0 uint32 // hash seed

 buckets    unsafe.Pointer // array of 2^B Buckets. may be nil if count==0.
 oldbuckets unsafe.Pointer // previous bucket array of half the size, non-nil only when growing
 nevacuate  uintptr        // progress counter for evacuation (buckets less than this have been evacuated)

 // If both key and value do not contain pointers and are inline, then we mark bucket
 // type as containing no pointers. This avoids scanning such maps.
 // However, bmap.overflow is a pointer. In order to keep overflow buckets
 // alive, we store pointers to all overflow buckets in hmap.overflow.
 // Overflow is used only if key and value do not contain pointers.
 // overflow[0] contains overflow buckets for hmap.buckets.
 // overflow[1] contains overflow buckets for hmap.oldbuckets.
 // The first indirection allows us to reduce static size of hmap.
 // The second indirection allows to store a pointer to the slice in hiter.
 overflow *[2]*[]*bmap
}

bucket內(nèi)部

bucket內(nèi)部
// A bucket for a Go map.
type bmap struct {
 tophash [bucketCnt]uint8
 // Followed by bucketCnt keys and then bucketCnt values.
 // NOTE: packing all the keys together and then all the values together makes the
 // code a bit more complicated than alternating key/value/key/value/... but it allows
 // us to eliminate padding which would be needed for, e.g., map[int64]int8.
 // Followed by an overflow pointer.
}

根據(jù)一個(gè)key得到value

func mapaccess1(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer
  • *maptype為map的類型信息掏呼,是編譯器在編譯期靜態(tài)生成的,里面包含了map的一些元信息铅檩,比如key和value的類型信息等等
  • *hmap為map的header憎夷,即map的引用
  • key是一個(gè)通用的指針,代表了key的引用
  • 返回值為一個(gè)指針昧旨,指向?qū)?yīng)的value引用

hash計(jì)算找到bucket

那我們怎么訪問到對應(yīng)的bucket呢拾给,我們需要得到對應(yīng)key的hash值

bucket的hash
alg := t.key.alg
hash := alg.hash(key, uintptr(h.hash0))
m := uintptr(1)<<h.B - 1
b := (*bmap)(add(h.buckets, (hash&m)*uintptr(t.bucketsize)))

根據(jù)tophash和key定位到具體的bucket

  • tophash可以快速試錯(cuò)祥得,如果tophash不相等直接跳過
  • tophash相等的話,根據(jù)key的比較來判斷是否相等鸣戴,如果相等則找到
  • 如果當(dāng)前bucket都試玩還沒有找到啃沪,則調(diào)到下一個(gè)bucket

擴(kuò)容

loadFactor %overflow bytes/entry hitprobe missprobe
4.00 2.13 20.77 3.00 4.00
4.50 4.05 17.30 3.25 4.50
5.00 6.85 14.77 3.50 5.00
5.50 10.55 12.94 3.75 5.50
6.00 15.27 11.67 4.00 6.00
6.50 20.90 10.79 4.25 6.50
7.00 27.14 10.15 4.50 7.00
7.50 34.03 9.73 4.75 7.50
8.00 41.10 9.40 5.00 8.00

各個(gè)參數(shù)的意思:

  • %overflow 溢出率,平均一個(gè)bucket有多少個(gè)kv的時(shí)候會(huì)溢出
  • bytes/entry 平均存一個(gè)kv需要額外存儲(chǔ)多少字節(jié)的數(shù)據(jù)
  • hitprobe 找到一個(gè)存在的key平均需要找?guī)紫?/li>
  • missprobe 找到一個(gè)不存在的key平均需要找?guī)紫?/li>

目前采用的是這一行:

| 6.50 | 20.90 | 10.79 | 4.25 | 6.50 |

遷移

遷移
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末窄锅,一起剝皮案震驚了整個(gè)濱河市创千,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌入偷,老刑警劉巖追驴,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異疏之,居然都是意外死亡殿雪,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進(jìn)店門锋爪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來丙曙,“玉大人,你說我怎么就攤上這事其骄】髁” “怎么了?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵拯爽,是天一觀的道長索抓。 經(jīng)常有香客問我,道長毯炮,這世上最難降的妖魔是什么逼肯? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮桃煎,結(jié)果婚禮上篮幢,老公的妹妹穿的比我還像新娘。我一直安慰自己备禀,他們只是感情好洲拇,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著曲尸,像睡著了一般赋续。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上另患,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天纽乱,我揣著相機(jī)與錄音,去河邊找鬼昆箕。 笑死鸦列,一個(gè)胖子當(dāng)著我的面吹牛租冠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播薯嗤,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼顽爹,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了骆姐?” 一聲冷哼從身側(cè)響起镜粤,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎玻褪,沒想到半個(gè)月后肉渴,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡带射,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年同规,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片窟社。...
    茶點(diǎn)故事閱讀 40,675評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡券勺,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出灿里,到底是詐尸還是另有隱情朱灿,我是刑警寧澤,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布钠四,位于F島的核電站,受9級特大地震影響跪楞,放射性物質(zhì)發(fā)生泄漏缀去。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一甸祭、第九天 我趴在偏房一處隱蔽的房頂上張望缕碎。 院中可真熱鬧,春花似錦池户、人聲如沸咏雌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赊抖。三九已至,卻和暖如春寨典,著一層夾襖步出監(jiān)牢的瞬間氛雪,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工耸成, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留报亩,地道東北人浴鸿。 一個(gè)月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像弦追,于是被迫代替她去往敵國和親岳链。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評論 2 360

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