溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何正確使用Go Map

發(fā)布時間:2021-10-12 14:33:12 來源:億速云 閱讀:134 作者:iii 欄目:編程語言

本篇內容主要講解“如何正確使用Go Map”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“如何正確使用Go Map”吧!

前言

例子如下:

func main() {  m := make(map[int32]string)  m[0] = "EDDYCJY1"  m[1] = "EDDYCJY2"  m[2] = "EDDYCJY3"  m[3] = "EDDYCJY4"  m[4] = "EDDYCJY5"   for k, v := range m {   log.Printf("k: %v, v: %v", k, v)  } }

假設運行這段代碼,輸出的結果是怎么樣?是有序,還是無序輸出呢?

k: 3, v: EDDYCJY4 k: 4, v: EDDYCJY5 k: 0, v: EDDYCJY1 k: 1, v: EDDYCJY2 k: 2, v: EDDYCJY3

從輸出結果上來講,是非固定順序輸出的,也就是每次都不一樣。但這是為什么呢?

首先建議你先自己想想原因。其次我在面試時聽過一些說法。有人說因為是哈希的所以就是無(亂)序等等說法。當時我是有點 ???

這也是這篇文章出現的原因,希望大家可以一起研討一下,理清這個問題 :)

看一下匯編

   ... 0x009b 00155 (main.go:11) LEAQ type.map[int32]string(SB), AX 0x00a2 00162 (main.go:11) PCDATA $2, $0 0x00a2 00162 (main.go:11) MOVQ AX, (SP) 0x00a6 00166 (main.go:11) PCDATA $2, $2 0x00a6 00166 (main.go:11) LEAQ ""..autotmp_3+24(SP), AX 0x00ab 00171 (main.go:11) PCDATA $2, $0 0x00ab 00171 (main.go:11) MOVQ AX, 8(SP) 0x00b0 00176 (main.go:11) PCDATA $2, $2 0x00b0 00176 (main.go:11) LEAQ ""..autotmp_2+72(SP), AX 0x00b5 00181 (main.go:11) PCDATA $2, $0 0x00b5 00181 (main.go:11) MOVQ AX, 16(SP) 0x00ba 00186 (main.go:11) CALL runtime.mapiterinit(SB) 0x00bf 00191 (main.go:11) JMP 207 0x00c1 00193 (main.go:11) PCDATA $2, $2 0x00c1 00193 (main.go:11) LEAQ ""..autotmp_2+72(SP), AX 0x00c6 00198 (main.go:11) PCDATA $2, $0 0x00c6 00198 (main.go:11) MOVQ AX, (SP) 0x00ca 00202 (main.go:11) CALL runtime.mapiternext(SB) 0x00cf 00207 (main.go:11) CMPQ ""..autotmp_2+72(SP), $0 0x00d5 00213 (main.go:11) JNE 193 ...

我們大致看一下整體過程,重點處理 Go map 循環(huán)迭代的是兩個 runtime 方法,如下:

  • runtime.mapiterinit

  • runtime.mapiternext

但你可能會想,明明用的是 for range 進行循環(huán)迭代,怎么出現了這兩個函數,怎么回事?

看一下轉換后

var hiter map_iteration_struct for mapiterinit(type, range, &hiter); hiter.key != nil; mapiternext(&hiter) {     index_temp = *hiter.key     value_temp = *hiter.val     index = index_temp     value = value_temp     original body }

實際上編譯器對于 slice 和 map 的循環(huán)迭代有不同的實現方式,并不是 for 一扔就完事了,還做了一些附加動作進行處理。而上述代碼就是 for  range map 在編譯器展開后的偽實現

看一下源碼

runtime.mapiterinit

func mapiterinit(t *maptype, h *hmap, it *hiter) {  ...  it.t = t  it.h = h  it.B = h.B  it.buckets = h.buckets  if t.bucket.kind&kindNoPointers != 0 {   h.createOverflow()   it.overflow = h.extra.overflow   it.oldoverflow = h.extra.oldoverflow  }   r := uintptr(fastrand())  if h.B > 31-bucketCntBits {   r += uintptr(fastrand()) << 31  }  it.startBucket = r & bucketMask(h.B)  it.offset = uint8(r >> h.B & (bucketCnt - 1))  it.bucket = it.startBucket     ...   mapiternext(it) }

通過對 mapiterinit 方法閱讀,可得知其主要用途是在 map  進行遍歷迭代時進行初始化動作。共有三個形參,用于讀取當前哈希表的類型信息、當前哈希表的存儲信息和當前遍歷迭代的數據

為什么

咱們關注到源碼中 fastrand 的部分,這個方法名,是不是迷之眼熟。沒錯,它是一個生成隨機數的方法。再看看上下文:

... // decide where to start r := uintptr(fastrand()) if h.B > 31-bucketCntBits {  r += uintptr(fastrand()) << 31 } it.startBucket = r & bucketMask(h.B) it.offset = uint8(r >> h.B & (bucketCnt - 1))  // iterator state it.bucket = it.startBucket

在這段代碼中,它生成了隨機數。用于決定從哪里開始循環(huán)迭代。更具體的話就是根據隨機數,選擇一個桶位置作為起始點進行遍歷迭代

因此每次重新 for range map,你見到的結果都是不一樣的。那是因為它的起始位置根本就不固定!

runtime.mapiternext

func mapiternext(it *hiter) {     ...     for ; i < bucketCnt; i++ {   ...   k := add(unsafe.Pointer(b), dataOffset+uintptr(offi)*uintptr(t.keysize))   v := add(unsafe.Pointer(b), dataOffset+bucketCnt*uintptr(t.keysize)+uintptr(offi)*uintptr(t.valuesize))   ...   if (b.tophash[offi] != evacuatedX && b.tophash[offi] != evacuatedY) ||    !(t.reflexivekey || alg.equal(k, k)) {    ...    it.key = k    it.value = v   } else {    rk, rv := mapaccessK(t, h, k)    if rk == nil {     continue // key has been deleted    }    it.key = rk    it.value = rv   }   it.bucket = bucket   if it.bptr != b {    it.bptr = b   }   it.i = i + 1   it.checkBucket = checkBucket   return  }  b = b.overflow(t)  i = 0  goto next }

到此,相信大家對“如何正確使用Go Map”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續(xù)學習!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI