map聲明數組
var m4 map[int]int //只是聲明 沒有開闢空間 m4[1]=100 //報錯 log.Println(m4)
建立安全
//1 m3:=make(map[int]string,100) //能夠指定長度 log.Println(len(m3)) //0 鍵值對的數量 m2:=make(map[string]string) //使用默認長度 m2["你"] = "你好啊" log.Println(m2) //2 d2 :=map[string]int{"one":1, "tow":2} //初始化 d3 :=map[int]int{} //建立了空map
判斷值是否存在併發
只接受一個的話默認返回的是value,兩個的話有existsapp
//判斷是否存在 val,exists :=d3["tow"] //若是不存在 返回零值 exists爲false
map遍歷ui
m5:=map[string]string{"one":"1","tow":"2","three":"3"} for k:=range m5{ //默認是key log.Println(k) //one tow... } for k,v:=range m5{ //k-v log.Println(k,v) // }
刪除this
m5:=map[string]string{"one":"1","tow":"2","three":"3"} delete(m5, "one")
go沒有內置set類型,可是能夠用map很輕鬆模仿,由於map的key是惟一的線程
type StrSet struct { data map[string]bool sync.RWMutex //讀寫鎖 保證線程安全 } func New() *StrSet { return &StrSet{ data: make(map[string]bool), } } func (this *StrSet)Add(val string) { this.Lock() defer this.Unlock() if this.data==nil{ this = New() } this.data[val] = true } func (this *StrSet)Delete(val string) { this.Lock() defer this.Unlock() if this.data==nil{ return } delete(this.data,val) } func (this *StrSet) IsExist(val string) bool { this.RLock() defer this.RUnlock() if this.data==nil{ return false } _,ok:=this.data[val] return ok } func (this *StrSet) GetAll() (result []string) { if this.data==nil{ return } for val :=range this.data{ result = append(result, val) } return } func (this *StrSet) Clear() { this.Lock() defer this.Unlock() this.data = map[string]bool{} } func main() { s:=New() s.Add("panbin") s.Add("biningo") s.Add("a") s.Add("b") s.Add("panbin") log.Println(s.GetAll()) }
借鑑了以下博客。寫的很好指針
深刻Go的Map使用和實現原理 code
先來觀摩一波map底層結構,第一眼確定萬臉懵逼😂three
// A header for a Go map. type hmap struct { count int // 元素個數 flags uint8 B uint8 //包含2^B個桶 指向bmap結構 用hash來散列k-v要到哪一個桶 noverflow uint16 //溢出的桶的個數 桶的數組可能會溢出 hash0 uint32 //hash種子 //桶數組的指針指向bmap結構 //bucket[0]->bucket[1]->... buckets unsafe.Pointer oldbuckets unsafe.Pointer //擴容的時候用於複製的buckets數組 nevacuate uintptr //搬遷進度(已經搬遷的buckets數量) extra *mapextra //用於擴容 若是元素過多 超過了buckets數組的範圍 就要擴容 }
mapextra 用於擴容的結構體指針
type mapextra struct { overflow *[]*bmap //擴容的地址 oldoverflow *[]*bmap //用於擴容 nextOverflow *bmap //鏈表連接的指針 }
bmap map存儲k或v的數組,
// A bucket for a Go map. type bmap struct { tophash [bucketCnt]uint8 }
map的實現過程
底層一個數組arr
index = hash(key)
arr[index] = struct{xxxx}
go map的每一個arr下面存的是一個 bucket
注意,這裏的value都會轉化爲byte類型,也就是uint8類型,key是int64類型的, 每一個bucket中能夠存儲8個kv鍵值對,
hash值的高八位存儲在bucket中的tophash中,用來快速判斷key是否存在
// A bucket for a Go map. type bmap struct { tophash [bucketCnt]uint8 //bucketCnt=8 [8]uint8 這個值會動態增長 }
當每一個bucket存儲的kv對到達8個以後,會經過指針指向一個新的bucket, 從而造成一個鏈表
這個指針事實上並無顯示定義,是經過指針運算進行訪問的。能夠想象成靜態鏈表類型,而且k-v對也是經過指針運算得出的,tophash只是用來檢查key是否存在
當往map中存儲一個kv對時,經過k獲取hash值,hash值的低八位和bucket數組長度取餘,定位到在數組中的那個下標,hash值的高八位存儲在bucket中的tophash中,用來快速判斷key是否存在,key和value的具體值則經過指針運算存儲,當一個bucket滿時,經過overfolw指針連接到下一個bucket。
map不是併發安全的
要支持併發,能夠用 sync.Map,裏面都是go的原子操做
sm:=sync.Map{} sm.Store("one","1") //無返回值 sm.Store("tow","2") sm.LoadOrStore("3","three") //取元素 若是沒有就存進去 並返回值 sm.Delete("1") //無返回值 log.Println(sm.Load("1")) log.Println(sm)