我可能並不會使用golang map

package main

import (
    "fmt"
)

func main(){
    mapa:= make(map[string]int, 10)
    // var mapa map[string]int
    mapa["zhao"] = 1
    mapa["qian"] = 2
    fmt.Println(mapa["li"])
}

複製代碼

看上面的例子,咱們可能存在的疑問有如下幾個:git

  • 1.make進行map的建立,後面的參數10是幹啥的,不一樣的值,有啥區別?不提供行不行?
  • 2.註釋掉的var申明的map能不能直接進行賦值操做?
  • 3.我獲取不存在的key,結果是怎樣的?
  • 4.最後一個終極問題,分配的底層數組用完以後,啥時候擴容?

等等,或許你還有其餘的疑問,咱們首先看看上面的幾個疑問吧github

先看看在make建立map時,參數爲10.具體執行的操做golang

0x0050 00080 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	PCDATA	$2, $1
	0x0050 00080 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	LEAQ	type.map[string]int(SB), AX
	0x0057 00087 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	PCDATA	$2, $0
	0x0057 00087 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	MOVQ	AX, (SP)
	0x005b 00091 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	MOVQ	$10, 8(SP)
	0x0064 00100 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	PCDATA	$2, $1
	0x0064 00100 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	PCDATA	$0, $0
	0x0064 00100 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	LEAQ	""..autotmp_2+136(SP), AX
	0x006c 00108 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	PCDATA	$2, $0
	0x006c 00108 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	MOVQ	AX, 16(SP)
	0x0071 00113 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	CALL	runtime.makemap(SB)
	0x0076 00118 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	PCDATA	$2, $1
	0x0076 00118 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	MOVQ	24(SP), AX
	0x007b 00123 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	PCDATA	$0, $2
	0x007b 00123 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	MOVQ	AX, "".mapa+56(SP)
複製代碼

執行的makemap調用,咱們看看這個函數的參數和返回值狀況,第一個參數是type.map[string]int(SB),第二個參數是10,第三個參數頗有意思數組

0x0064 00100 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	PCDATA	$0, $0
	0x0064 00100 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	LEAQ	""..autotmp_2+136(SP), AX
	0x006c 00108 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	PCDATA	$2, $0
	0x006c 00108 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	MOVQ	AX, 16(SP)
複製代碼

暫且不論它是啥,先看看makemap函數的原型bash

// makemap 實現了Go map的建立,經過make(map[k]v, hint).
// 若是編譯器肯定該映射或第一個存儲桶,能夠在堆棧上建立,h和/或bucket能夠爲非nil。
// 若是參數h 不爲nil,map能夠直接在h中建立。
// 若是h.buckets 不爲nil,bucket指針指向的能夠用於做爲第一個桶。
func makemap(t *maptype, hint int, h *hmap) *hmap {
	mem, overflow := math.MulUintptr(uintptr(hint), t.bucket.size)
	if overflow || mem > maxAlloc {
		hint = 0
	}

	// initialize Hmap
	if h == nil {
		h = new(hmap)
	}
	h.hash0 = fastrand()

	// Find the size parameter B which will hold the requested # of elements.
	// For hint < 0 overLoadFactor returns false since hint < bucketCnt.
	B := uint8(0)
	for overLoadFactor(hint, B) {
		B++
	}
	h.B = B

	// allocate initial hash table
	// if B == 0, the buckets field is allocated lazily later (in mapassign)
	// If hint is large zeroing this memory could take a while.
	if h.B != 0 {
		var nextOverflow *bmap
		h.buckets, nextOverflow = makeBucketArray(t, h.B, nil)
		if nextOverflow != nil {
			h.extra = new(mapextra)
			h.extra.nextOverflow = nextOverflow
		}
	}

	return h
}
複製代碼

從原型中看,第三個參數是hmap指針,這個東西是啥呢,若是h爲nil,還須要new一個?數據結構

const (
    // 一個桶中能夠容納的最大數據的key/value對
	bucketCntBits = 3
	bucketCnt     = 1 << bucketCntBits
	...
)
// A header for a Go map.
type hmap struct {
	count     int // 元素個數,必須位於第一項,調用 len(map) 時,直接返回此值
	flags     uint8
	B         uint8  // B 是 buckets 數組的長度以2爲底的對數, (能夠容納 loadFactor * 2^B 個元素)
	noverflow uint16 // 溢出桶的大概數量
	hash0     uint32 // 哈希種子

	buckets    unsafe.Pointer // 數據存儲桶
	oldbuckets unsafe.Pointer // 擴容前的桶,只有在擴容的時候非空。
	nevacuate  uintptr        // progress counter for evacuation (buckets less than this have been evacuated)

	extra *mapextra // 可選字段
}

// 桶的數據結構原型結構體.
type bmap struct {
    // tophash 一般包含桶中每一個鍵的hash值的最高字節(8位)
	tophash [bucketCnt]uint8   // 8字節長度的數組
	// 以後是bucketCnt數量的鍵,而後是bucketCnt數量的值。
	// 後跟一個溢出指針。
}

複製代碼

overLoadFactor函數的原型爲app

const (
...
    // 觸發增加的存儲桶的最大平均負載爲6.5。
	// 表示爲loadFactorNum / loadFactDen,以容許整數數學運算。
	loadFactorNum = 13
	loadFactorDen = 2
...
)


// bucketShift返回1 << b,已針對代碼生成進行了優化。
func bucketShift(b uint8) uintptr {
	if sys.GoarchAmd64|sys.GoarchAmd64p32|sys.Goarch386 != 0 {
		b &= sys.PtrSize*8 - 1 // help x86 archs remove shift overflow checks
	}
	return uintptr(1) << b
}

// overLoadFactor報告count個項目放在1<<B的桶中,是否超過負載因子
func overLoadFactor(count int, B uint8) bool {
	return count > bucketCnt && uintptr(count) > loadFactorNum*(bucketShift(B)/loadFactorDen)
}
複製代碼

首先判斷count是否大於bucketCnt,上述例子中,count就是hint,就是傳入的參數10.bucketCnt是單個桶能容納的最大鍵值對,爲8.條件成立後,繼續判斷count是否大於6.5*(1<<b)若是大於返回true不然返回falseless

makemap函數中,在對h的B值進行賦值以前,進行了一個檢測overLoadFactor循環,最終肯定下來的B值爲1.而B是 buckets 數組的長度以2爲底的對數, (能夠容納 loadFactor * 2^B 個元素).而B不爲0,會進行桶數組的分配操做函數

// makeBucketArray初始化map存儲區的底層數組。
// 1<<b是要分配桶的最小數據
// dirtyalloc 應該是nil或者是一個以前使用的t和b參數已經分配的桶數組
// 若是dirtyalloc是nil 表示一個新的桶數組將會分配,
// 不然dirtyalloc將被清空並做爲桶數組重用
func makeBucketArray(t *maptype, b uint8, dirtyalloc unsafe.Pointer) (buckets unsafe.Pointer, nextOverflow *bmap) {
	base := bucketShift(b)
	nbuckets := base
	// 對於小b,溢出桶不太可能出現。避免計算的開銷。
	if b >= 4 {
		// Add on the estimated number of overflow buckets
		// required to insert the median number of elements
		// used with this value of b.
		nbuckets += bucketShift(b - 4)
		sz := t.bucket.size * nbuckets
		up := roundupsize(sz)
		if up != sz {
			nbuckets = up / t.bucket.size
		}
	}

	if dirtyalloc == nil {
		buckets = newarray(t.bucket, int(nbuckets))
	} else {
		// dirtyalloc was previously generated by
		// the above newarray(t.bucket, int(nbuckets))
		// but may not be empty.
		buckets = dirtyalloc
		size := t.bucket.size * nbuckets
		if t.bucket.kind&kindNoPointers == 0 {
			memclrHasPointers(buckets, size)
		} else {
			memclrNoHeapPointers(buckets, size)
		}
	}

	if base != nbuckets {
		// We preallocated some overflow buckets.
		// To keep the overhead of tracking these overflow buckets to a minimum,
		// we use the convention that if a preallocated overflow bucket's overflow // pointer is nil, then there are more available by bumping the pointer. // We need a safe non-nil pointer for the last overflow bucket; just use buckets. nextOverflow = (*bmap)(add(buckets, base*uintptr(t.bucketsize))) last := (*bmap)(add(buckets, (nbuckets-1)*uintptr(t.bucketsize))) last.setoverflow(t, (*bmap)(buckets)) } return buckets, nextOverflow } 複製代碼

初次調用的時候,dirtyalloc參數是nil,會首次建立一個數組oop

if dirtyalloc == nil {
		buckets = newarray(t.bucket, int(nbuckets))
	}
複製代碼

後續擴容的時候,其所指的地址爲舊桶的地址,回到咱們以前的問題,mapa:= make(map[string]int, 10)

type hmap struct {
	count     int // 0
	flags     uint8
	B         uint8  // 1
	noverflow uint16 // 溢出桶的大概數量
	hash0     uint32 // 哈希種子

	buckets    unsafe.Pointer // 數據存儲桶
	oldbuckets unsafe.Pointer // 擴容前的桶,只有在擴容的時候非空。
	nevacuate  uintptr     

	extra *mapextra // 可選字段
}
複製代碼

再來看看第一個問題,這個參數10,是用來在分配底層桶數組時,分配幾個桶數組,爲10時,B的值爲1,分配兩個桶數組,每一個桶數組8個鍵值對,足夠使用了,若是爲20,則B的值爲2,底層會分配4個桶數組。若是參數爲5時,B的值爲1,分配兩個桶...

固然了若是在make()建立map的時候,不提供第二個參數也是能夠的,可是你會發現,在提供的參數小於bucketCnt時,彙編後是看不到makemap的函數調用的。

0x0032 00050 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	PCDATA	$0, $1
	0x0032 00050 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	XORPS	X1, X1
	0x0035 00053 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	MOVUPS	X1, ""..autotmp_3+136(SP)
	0x003d 00061 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	XORPS	X1, X1
	0x0040 00064 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	MOVUPS	X1, ""..autotmp_3+152(SP)
	0x0048 00072 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	XORPS	X1, X1
	0x004b 00075 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	MOVUPS	X1, ""..autotmp_3+168(SP)
	0x0053 00083 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	PCDATA	$2, $1
	0x0053 00083 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	PCDATA	$0, $2
	0x0053 00083 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	LEAQ	""..autotmp_4+184(SP), DI
	0x005b 00091 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	XORPS	X0, X0
	0x005e 00094 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	PCDATA	$2, $0
	0x005e 00094 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	LEAQ	-48(DI), DI
	0x0062 00098 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	DUFFZERO	$239
	0x0075 00117 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	PCDATA	$2, $2
	0x0075 00117 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	LEAQ	""..autotmp_3+136(SP), AX
	0x007d 00125 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	PCDATA	$2, $0
	0x007d 00125 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	TESTB	AL, (AX)
	0x007f 00127 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	PCDATA	$2, $2
	0x007f 00127 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	PCDATA	$0, $1
	0x007f 00127 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	LEAQ	""..autotmp_4+184(SP), AX
	0x0087 00135 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	PCDATA	$2, $0
	0x0087 00135 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	MOVQ	AX, ""..autotmp_3+152(SP)
	0x008f 00143 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	PCDATA	$2, $2
	0x008f 00143 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	LEAQ	""..autotmp_3+136(SP), AX
	0x0097 00151 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	PCDATA	$2, $0
	0x0097 00151 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	PCDATA	$0, $3
	0x0097 00151 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	MOVQ	AX, ""..autotmp_5+88(SP)
	0x009c 00156 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	CALL	runtime.fastrand(SB)
	0x00a1 00161 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	PCDATA	$2, $2
	0x00a1 00161 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	PCDATA	$0, $1
	0x00a1 00161 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	MOVQ	""..autotmp_5+88(SP), AX
	0x00a6 00166 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	TESTB	AL, (AX)
	0x00a8 00168 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	MOVL	(SP), CX
	0x00ab 00171 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	PCDATA	$2, $0
	0x00ab 00171 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	MOVL	CX, 12(AX)
	0x00ae 00174 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	PCDATA	$2, $2
	0x00ae 00174 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	PCDATA	$0, $0
	0x00ae 00174 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	LEAQ	""..autotmp_3+136(SP), AX
	0x00b6 00182 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	PCDATA	$2, $0
	0x00b6 00182 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	PCDATA	$0, $4
	0x00b6 00182 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:8)	MOVQ	AX, "".mapa+56(SP)
複製代碼

能夠看到,直到進行mapa賦值操做,都沒有看到makemap的函數調用。惟一的調用可能就是fastrand. 編譯器肯定 make map 函數的位置在:cmd/compile/internal/gc/walk.go:1199(go version go1.12.1 darwin/amd64):

case OMAKEMAP:
		t := n.Type
		hmapType := hmap(t)
		hint := n.Left

		// var h *hmap
		var h *Node
		if n.Esc == EscNone {
			// Allocate hmap on stack.

			// var hv hmap
			hv := temp(hmapType)
			zero := nod(OAS, hv, nil)
			zero = typecheck(zero, ctxStmt)
			init.Append(zero)
			// h = &hv
			h = nod(OADDR, hv, nil)

			// Allocate one bucket pointed to by hmap.buckets on stack if hint
			// is not larger than BUCKETSIZE. In case hint is larger than
			// BUCKETSIZE runtime.makemap will allocate the buckets on the heap.
			// Maximum key and value size is 128 bytes, larger objects
			// are stored with an indirection. So max bucket size is 2048+eps.
			if !Isconst(hint, CTINT) ||
				hint.Val().U.(*Mpint).CmpInt64(BUCKETSIZE) <= 0 {
				// var bv bmap
				bv := temp(bmap(t))

				zero = nod(OAS, bv, nil)
				zero = typecheck(zero, ctxStmt)
				init.Append(zero)

				// b = &bv
				b := nod(OADDR, bv, nil)

				// h.buckets = b
				bsym := hmapType.Field(5).Sym // hmap.buckets see reflect.go:hmap
				na := nod(OAS, nodSym(ODOT, h, bsym), b)
				na = typecheck(na, ctxStmt)
				init.Append(na)
			}
		}

		if Isconst(hint, CTINT) && hint.Val().U.(*Mpint).CmpInt64(BUCKETSIZE) <= 0 {
			// Handling make(map[any]any) and
			// make(map[any]any, hint) where hint <= BUCKETSIZE
			// special allows for faster map initialization and
			// improves binary size by using calls with fewer arguments.
			// For hint <= BUCKETSIZE overLoadFactor(hint, 0) is false
			// and no buckets will be allocated by makemap. Therefore,
			// no buckets need to be allocated in this code path.
			if n.Esc == EscNone {
				// Only need to initialize h.hash0 since
				// hmap h has been allocated on the stack already.
				// h.hash0 = fastrand()
				rand := mkcall("fastrand", types.Types[TUINT32], init)
				hashsym := hmapType.Field(4).Sym // hmap.hash0 see reflect.go:hmap
				a := nod(OAS, nodSym(ODOT, h, hashsym), rand)
				a = typecheck(a, ctxStmt)
				a = walkexpr(a, init)
				init.Append(a)
				n = convnop(h, t)
			} else {
				// Call runtime.makehmap to allocate an
				// hmap on the heap and initialize hmap's hash0 field. fn := syslook("makemap_small") fn = substArgTypes(fn, t.Key(), t.Elem()) n = mkcall1(fn, n.Type, init) } } else { if n.Esc != EscNone { h = nodnil() } // Map initialization with a variable or large hint is // more complicated. We therefore generate a call to // runtime.makemap to intialize hmap and allocate the // map buckets. // When hint fits into int, use makemap instead of // makemap64, which is faster and shorter on 32 bit platforms. fnname := "makemap64" argtype := types.Types[TINT64] // Type checking guarantees that TIDEAL hint is positive and fits in an int. // See checkmake call in TMAP case of OMAKE case in OpSwitch in typecheck1 function. // The case of hint overflow when converting TUINT or TUINTPTR to TINT // will be handled by the negative range checks in makemap during runtime. if hint.Type.IsKind(TIDEAL) || maxintval[hint.Type.Etype].Cmp(maxintval[TUINT]) <= 0 { fnname = "makemap" argtype = types.Types[TINT] } fn := syslook(fnname) fn = substArgTypes(fn, hmapType, t.Key(), t.Elem()) n = mkcall1(fn, n.Type, init, typename(n.Type), conv(hint, argtype), h) } 複製代碼

再來看第二個問題,若是使用var 進行變量聲明以後,是否能夠直接鍵值對賦值呢,slice進行聲明以後就行啊,是否是map也能夠?

var mapa map[string]int
    mapa["zhao"] = 1
複製代碼

運行報錯:

panic: assignment to entry in nil map

goroutine 1 [running]:
main.main()
	/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:11 +0x4f
exit status 2
複製代碼

但從錯誤信息上,咱們能夠得知,在nil數組上,進行分配操做。

0x002f 00047 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:9)	MOVQ	$0, "".mapa+56(SP)
複製代碼

var聲明的map變量是一個nil map.

0x002f 00047 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:9)	PCDATA	$0, $1
	0x002f 00047 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:9)	MOVQ	$0, "".mapa+56(SP)
	0x0038 00056 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:11)	PCDATA	$2, $1
	0x0038 00056 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:11)	LEAQ	type.map[string]int(SB), AX
	0x003f 00063 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:11)	PCDATA	$2, $0
	0x003f 00063 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:11)	MOVQ	AX, (SP)
	0x0043 00067 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:11)	MOVQ	$0, 8(SP)
	0x004c 00076 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:11)	PCDATA	$2, $1
	0x004c 00076 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:11)	LEAQ	go.string."zhao"(SB), AX
	0x0053 00083 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:11)	PCDATA	$2, $0
	0x0053 00083 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:11)	MOVQ	AX, 16(SP)
	0x0058 00088 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:11)	MOVQ	$4, 24(SP)
	0x0061 00097 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:11)	CALL	runtime.mapassign_faststr(SB)
複製代碼

在進行鍵值對賦值的操做上,調用的是CALL runtime.mapassign_faststr(SB)

func mapassign_faststr(t *maptype, h *hmap, s string) unsafe.Pointer {
	if h == nil {
		panic(plainError("assignment to entry in nil map"))
	}
	if raceenabled {
		callerpc := getcallerpc()
		racewritepc(unsafe.Pointer(h), callerpc, funcPC(mapassign_faststr))
	}
	if h.flags&hashWriting != 0 {
		throw("concurrent map writes")
	}
	// 計算key的hash值
	key := stringStructOf(&s)
	hash := t.key.alg.hash(noescape(unsafe.Pointer(&s)), uintptr(h.hash0))

	// Set hashWriting after calling alg.hash for consistency with mapassign.
	h.flags ^= hashWriting

    // 若是桶數組爲空,分配長度爲1的桶數組。
	if h.buckets == nil {
		h.buckets = newobject(t.bucket) // newarray(t.bucket, 1)
	}

again:
    // 計算低 B 位 hash,根據計算出的 bucketMask 選擇對應的 bucket
    // bucketMask returns 1<<h.B - 1
	bucket := hash & bucketMask(h.B)
	if h.growing() {
		growWork_faststr(t, h, bucket)
	}
	// 計算出存儲的 bucket 的內存位置
    // pos = start + bucketNumber * bucetsize
	b := (*bmap)(unsafe.Pointer(uintptr(h.buckets) + bucket*uintptr(t.bucketsize)))
	// 計算高 8 位 hash
	top := tophash(hash)

	var insertb *bmap
	var inserti uintptr
	var insertk unsafe.Pointer

bucketloop:
	for {
		for i := uintptr(0); i < bucketCnt; i++ {
			if b.tophash[i] != top {
				if isEmpty(b.tophash[i]) && insertb == nil {
					insertb = b
					inserti = i
				}
				if b.tophash[i] == emptyRest {
					break bucketloop
				}
				continue
			}
			k := (*stringStruct)(add(unsafe.Pointer(b), dataOffset+i*2*sys.PtrSize))
			if k.len != key.len {
				continue
			}
			if k.str != key.str && !memequal(k.str, key.str, uintptr(key.len)) {
				continue
			}
			// already have a mapping for key. Update it.
			inserti = i
			insertb = b
			goto done
		}
		ovf := b.overflow(t)
		if ovf == nil {
			break
		}
		b = ovf
	}

	// Did not find mapping for key. Allocate new cell & add entry.

	// If we hit the max load factor or we have too many overflow buckets,
	// and we're not already in the middle of growing, start growing. if !h.growing() && (overLoadFactor(h.count+1, h.B) || tooManyOverflowBuckets(h.noverflow, h.B)) { hashGrow(t, h) goto again // Growing the table invalidates everything, so try again } if insertb == nil { // all current buckets are full, allocate a new one. insertb = h.newoverflow(t, b) inserti = 0 // not necessary, but avoids needlessly spilling inserti } insertb.tophash[inserti&(bucketCnt-1)] = top // mask inserti to avoid bounds checks insertk = add(unsafe.Pointer(insertb), dataOffset+inserti*2*sys.PtrSize) // store new key at insert position *((*stringStruct)(insertk)) = *key h.count++ done: val := add(unsafe.Pointer(insertb), dataOffset+bucketCnt*2*sys.PtrSize+inserti*uintptr(t.valuesize)) if h.flags&hashWriting == 0 { throw("concurrent map writes") } h.flags &^= hashWriting return val } 複製代碼

該函數首先判斷的就是參數h的值是否爲nil.也就是第二個參數,

0x0043 00067 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:11) MOVQ	$0, 8(SP)
複製代碼

第三個問題,獲取不到的key,如何處理

0x00c8 00200 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:26)	PCDATA	$0, $0
	0x00c8 00200 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:26)	MOVQ	"".mapa+56(SP), AX
	0x00cd 00205 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:26)	PCDATA	$2, $0
	0x00cd 00205 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:26)	MOVQ	AX, 8(SP)
	0x00d2 00210 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:26)	PCDATA	$2, $1
	0x00d2 00210 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:26)	LEAQ	go.string."li"(SB), AX
	0x00d9 00217 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:26)	PCDATA	$2, $0
	0x00d9 00217 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:26)	MOVQ	AX, 16(SP)
	0x00de 00222 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:26)	MOVQ	$2, 24(SP)
	0x00e7 00231 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:26)	CALL	runtime.mapaccess1_faststr(SB)
	0x00ec 00236 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:26)	PCDATA	$2, $1
	0x00ec 00236 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:26)	MOVQ	32(SP), AX
複製代碼

獲取鍵值,調用mapaccess1_faststr函數,該函數第一個參數是type.map[string]int(SB),第二個參數就是咱們的map變量.

0x00c8 00200 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:26)	PCDATA	$0, $0
	0x00c8 00200 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:26)	MOVQ	"".mapa+56(SP), AX
	0x00cd 00205 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:26)	PCDATA	$2, $0
	0x00cd 00205 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:26)	MOVQ	AX, 8(SP)
複製代碼

第三個參數是key的string。

0x00d2 00210 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:26)	PCDATA	$2, $1
	0x00d2 00210 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:26)	LEAQ	go.string."li"(SB), AX
	0x00d9 00217 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:26)	PCDATA	$2, $0
	0x00d9 00217 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:26)	MOVQ	AX, 16(SP)
	0x00de 00222 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:26)	MOVQ	$2, 24(SP)
	0x00e7 00231 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:26)	CALL	runtime.mapaccess1_faststr(SB)
複製代碼

調用mapaccess1_faststr,該函數首先在桶數組中檢查每一個key,當前桶檢查完畢後,繼續溢出桶的檢查,若是發現都沒有這個key ,則返回一個unsafe.Pointer的零值。

return unsafe.Pointer(&zeroVal[0])
複製代碼

若是發現了參數key,會返回該參數key對應的value地址。

0x00f6 00246 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:11)	PCDATA	$2, $0
	0x00f6 00246 (/Users/zhaojunwei/workspace/src/just.for.test/maptest/demo.go:11)	MOVQ	$1, (AX)
複製代碼

而後把賦的值放到該寄存器中。

放一張曹大的一張關於map的全瞰圖,更加明白map的底層結構

┌─────────────┐                                                                                                                                                                                                           
          │    hmap     │                                                                                                                                                                                                           
          ├─────────────┴──────────────────┐           ┌───────────────┐                                        ┌─────────┐                               ┌─────────┐                                                               
          │           count int            │           │               │                     ┌─────────────────▶│  bmap   │                          ┌───▶│  bmap   │                                                               
          │                                │           │               ▼                     │                  ├─────────┴─────────────────────┐    │    ├─────────┴─────────────────────┐                                         
          ├────────────────────────────────┤           │    ────────┬─────┐                  │                  │   tophash [bucketCnt]uint8    │    │    │   tophash [bucketCnt]uint8    │                                         
          │          flags uint8           │           │       ▲    │  0  │                  │                  │                               │    │    │                               │                                         
          │                                │           │       │    │     │──────────────────┘                  ├──────────┬────────────────────┤    │    ├──────────┬────────────────────┤                                         
          ├────────────────────────────────┤           │       │    ├─────┤                                     │   keys   │                    │    │    │   keys   │                    │                                         
          │            B uint8             │           │       │    │  1  │                                     ├───┬───┬──┴┬───┬───┬───┬───┬───┤    │    ├───┬───┬──┴┬───┬───┬───┬───┬───┤                                         
          │                                │           │       │    │     │──────────────────┐                  │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │    │    │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │                                         
          ├────────────────────────────────┤           │       │    ├─────┤                  │                  ├───┴───┴──┬┴───┴───┴───┴───┴───┤    │    ├───┴───┴──┬┴───┴───┴───┴───┴───┤                                         
          │        noverflow uint16        │           │       │    │  2  │                  │                  │  values  │                    │    │    │  values  │                    │                                         
          │                                │           │       │    │     │                  │                  ├───┬───┬──┴┬───┬───┬───┬───┬───┤    │    ├───┬───┬──┴┬───┬───┬───┬───┬───┤                                         
          ├────────────────────────────────┤           │       │    ├─────┤                  │                  │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │    │    │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │                                         
          │          hash0 uint32          │           │       │    │  3  │                  │                  ├───┴───┴───┴───┴───┴───┴───┴───┤    │    ├───┴───┴───┴───┴───┴───┴───┴───┤                                         
          │                                │           │       │    │     │                  │                  │        overflow *bmap         │    │    │        overflow *bmap         │                                         
          ├────────────────────────────────┤           │       │    ├─────┤                  │                  │                               │────┘    │                               │                                         
          │     buckets unsafe.Pointer     │           │       │    │  4  │                  │                  ├─────────┬─────────────────────┘         └───────────────────────────────┘                                         
          │                                │───────────┘       │    │     │                  └─────────────────▶│  bmap   │                                                                                                         
          ├────────────────────────────────┤                        ├─────┤                                     ├─────────┴─────────────────────┐                                                                                   
          │   oldbuckets unsafe.Pointer    │                        │  5  │                                     │   tophash [bucketCnt]uint8    │                                                                                   
          │                                │                        │     │                                     │                               │                                                                                   
          ├────────────────────────────────┤         size = 2 ^ B   ├─────┤                                     ├──────────┬────────────────────┤                                                                                   
          │       nevacuate uintptr        │                        │  6  │                                     │   keys   │                    │                                                                                   
          │                                │                        │     │                                     ├───┬───┬──┴┬───┬───┬───┬───┬───┤                                                                                   
          ├────────────────────────────────┤                   │    ├─────┤                                     │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │                                                                                   
          │        extra *mapextra         │                   │    │  7  │                                     ├───┴───┴──┬┴───┴───┴───┴───┴───┤                                                                                   
       ┌──│                                │                   │    │     │                                     │  values  │                    │                                                                                   
       │  └────────────────────────────────┘                   │    └─────┘                                     ├───┬───┬──┴┬───┬───┬───┬───┬───┤                                                                                   
       │                                                       │      ....                                      │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │                                                                                   
       │                                                       │                                                ├───┴───┴───┴───┴───┴───┴───┴───┤                                                                                   
       │                                                       │    ┌─────┐                                     │        overflow *bmap         │                                                                                   
       │                                                       │    │ 61  │                                     │                               │                                                                                   
       │                                                       │    │     │                                     └───────────────────────────────┘                                                                                   
       ▼                                                       │    ├─────┤                                               ............                                                                                              
┌─────────────┐                                                │    │ 62  │                                     ┌─────────┐                               ┌─────────┐                              ┌─────────┐                      
│  mapextra   │                                                │    │     │────────────────────────────────────▶│  bmap   │                          ┌───▶│  bmap   │                         ┌───▶│  bmap   │                      
├─────────────┴──────────────┐                                 │    ├─────┤                                     ├─────────┴─────────────────────┐    │    ├─────────┴─────────────────────┐   │    ├─────────┴─────────────────────┐
│     overflow *[]*bmap      │                                 │    │ 63  │                                     │   tophash [bucketCnt]uint8    │    │    │   tophash [bucketCnt]uint8    │   │    │   tophash [bucketCnt]uint8    │
│                            │                                 ▼    │     │──────────────────┐                  │                               │    │    │                               │   │    │                               │
├────────────────────────────┤                              ────────┴─────┘                  │                  ├──────────┬────────────────────┤    │    ├──────────┬────────────────────┤   │    ├──────────┬────────────────────┤
│    oldoverflow *[]*bmap    │                                                               │                  │   keys   │                    │    │    │   keys   │                    │   │    │   keys   │                    │
│                            │                                                               │                  ├───┬───┬──┴┬───┬───┬───┬───┬───┤    │    ├───┬───┬──┴┬───┬───┬───┬───┬───┤   │    ├───┬───┬──┴┬───┬───┬───┬───┬───┤
├────────────────────────────┤                                                               │                  │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │    │    │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │   │    │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │
│     nextoverflow *bmap     │                                                               │                  ├───┴───┴──┬┴───┴───┴───┴───┴───┤    │    ├───┴───┴──┬┴───┴───┴───┴───┴───┤   │    ├───┴───┴──┬┴───┴───┴───┴───┴───┤
│                            │                                                               │                  │  values  │                    │    │    │  values  │                    │   │    │  values  │                    │
└────────────────────────────┘                                                               │                  ├───┬───┬──┴┬───┬───┬───┬───┬───┤    │    ├───┬───┬──┴┬───┬───┬───┬───┬───┤   │    ├───┬───┬──┴┬───┬───┬───┬───┬───┤
                                                                                             │                  │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │    │    │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │   │    │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │
                                                                                             │                  ├───┴───┴───┴───┴───┴───┴───┴───┤    │    ├───┴───┴───┴───┴───┴───┴───┴───┤   │    ├───┴───┴───┴───┴───┴───┴───┴───┤
                                                                                             │                  │        overflow *bmap         │    │    │        overflow *bmap         │   │    │        overflow *bmap         │
                                                                                             │                  │                               │────┘    │                               │───┘    │                               │
                                                                                             │                  ├─────────┬─────────────────────┘         └───────────────────────────────┘        └───────────────────────────────┘
                                                                                             └─────────────────▶│  bmap   │                                                                                                         
                                                                                                                ├─────────┴─────────────────────┐                                                                                   
                                                                                                                │   tophash [bucketCnt]uint8    │                                                                                   
                                                                                                                │                               │                                                                                   
                                                                                                                ├──────────┬────────────────────┤                                                                                   
                                                                                                                │   keys   │                    │                                                                                   
                                                                                                                ├───┬───┬──┴┬───┬───┬───┬───┬───┤                                                                                   
                                                                                                                │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │                                                                                   
                                                                                                                ├───┴───┴──┬┴───┴───┴───┴───┴───┤                                                                                   
                                                                                                                │  values  │                    │                                                                                   
                                                                                                                ├───┬───┬──┴┬───┬───┬───┬───┬───┤                                                                                   
                                                                                                                │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │                                                                                   
                                                                                                                ├───┴───┴───┴───┴───┴───┴───┴───┤                                                                                   
                                                                                                                │        overflow *bmap         │                                                                                   
                                                                                                                │                               │                                                                                   
                                                                                                                └───────────────────────────────┘                                                                                   

複製代碼

最後一個問題,有關數組的擴容問題。咱們初始時make(map, 10),其底層的桶數組長度爲2。每一個桶能容納的鍵值對爲8個,也就是說,咱們的桶數組用完了,也只能存儲16個鍵值對,可是,其觸發擴容時機大體在何時呢?還要再看看runtime.mapassign_faststr.

// overLoadFactor reports whether count items placed in 1<<B buckets is over loadFactor.
func overLoadFactor(count int, B uint8) bool {
	return count > bucketCnt && uintptr(count) > loadFactorNum*(bucketShift(B)/loadFactorDen)
}

// tooManyOverflowBuckets reports whether noverflow buckets is too many for a map with 1<<B buckets.
// Note that most of these overflow buckets must be in sparse use;
// if use was dense, then we'd have already triggered regular map growth. func tooManyOverflowBuckets(noverflow uint16, B uint8) bool { // If the threshold is too low, we do extraneous work. // If the threshold is too high, maps that grow and shrink can hold on to lots of unused memory. // "too many" means (approximately) as many overflow buckets as regular buckets. // See incrnoverflow for more details. if B > 15 { B = 15 } // The compiler doesn't see here that B < 16; mask B to generate shorter shift code.
	return noverflow >= uint16(1)<<(B&15)
}

// growing reports whether h is growing. The growth may be to the same size or bigger.
func (h *hmap) growing() bool {
	return h.oldbuckets != nil
}

func mapassign_faststr(t *maptype, h *hmap, s string) unsafe.Pointer {
...
// Did not find mapping for key. Allocate new cell & add entry.

	// If we hit the max load factor or we have too many overflow buckets,
	// and we're not already in the middle of growing, start growing. if !h.growing() && (overLoadFactor(h.count+1, h.B) || tooManyOverflowBuckets(h.noverflow, h.B)) { hashGrow(t, h) goto again // Growing the table invalidates everything, so try again } ... } 複製代碼

若是達到了最大的負載因子,或者咱們有太多的溢出桶,同時並無在擴容,那麼就開始擴容。 計算過程,len(map)>8 && len(map)>6.5*(1<<B) 就會達到最大負載因子。 hmap的noverflow字段是用來記錄溢出桶個數的,當溢出桶個數達到uint16(1)<<(B&15)本示例中,B=1,若是溢出桶的數量大於等於2,也就認爲溢出桶過多,也會觸發響應的擴容。

兩種狀況官方採用了不一樣的擴容方案:

達到最大負載因子,將 B + 1,進而 hmap 的 bucket 數組擴容一倍; 有太多的溢出桶,經過移動 bucket 內容,使其傾向於緊密排列從而提升 bucket 利用率。

最終的哈希表的數據複製是由growWork()evacuate()來進行的。

固然,有關map的內容,還有不少,越深刻愈加現其實本身的知識儲備還有很大的進步空間,更多詳細內容,歡迎去曹大github上學習探究。有任何問題,歡迎留言。。。。

本系列文章:

有任何問題,歡迎留言

參考文獻:

相關文章
相關標籤/搜索