您好,登錄后才能下訂單哦!
golang基本數據結構Map也叫字典,字典的聲明格式如下: map[KeyType]ValueType
字典是無序鍵值對集合,字典要求KeyType必須是支持相等運算符(==,!=)的數據類型,比如:數字、字符串、指針、數組、結構體以及對應的接口類型,而ValueType可以是任意類型,字典也是引用類型,使用make函數或者初始化表達式語句來創建。比如:
{ m := make(map[string]int) //創建一個字典 m["a"] = 1 m["b"] = 2 m2 := map[int]struct{ //匿名結構體 x int }{ 1: {x:100}, 2: {x:200}, } fmt.Println(m,m2) }
字典的基本操作
比如:
{ m := make(map[string]int) m["route"] = 66 //添加key i := m["route"] //讀取key j := m["root"] //the value type is int, so the zero value is 0 n := len(m) //獲取長度 //n := cap(m) // ??? 引發一個思考 delete(m, "route") //刪除key }
如果訪問不存在的鍵,不會引發錯誤,而會默認返回ValueType的零值,那么還能否根據零值來判斷key的存在呢?
在讀取map操作時,可以使用雙返回值來正常讀取map,比如:
{ j, ok := m["root"] } 說明: ok是個bool類型變量,如果key真實存在,則ok的值為true,反之為false,如果你只是想測試key是否存在而不 獲取值的話,可以使用忽略字符"_"。
在修改map值操作時,因為受內存訪問安全和哈希算法等緣故,字典被設計成"not adrressable",因此不能直接修改value成員(結構體或數組)。比如:
{ m := map[int] user { 1 : { name:"tom", age:19}, } m[1].age = 1 //cannot assign to struct field m[1].age in map }
但是有兩種方式可以實現直接修改map的value成員:
是對整個value進行重新復制
聲明map時valueType為指針類型
{ m := map[int] user { 1 : { name:"tom", age:19}, } u := m[1] u.age = 1 m[1] = u } { m := map[int] *user { 1 : { name:"tom", age:19}, } m[1].age += 1 }
不能對nil字典進行寫操作,但是可以讀,比如:
{ var m map[string]int //p := m["a"] //ok m["a"] = 1 //panic: assignment to entry in nil map }
map遍歷:
{ // var m = make(map[string]int) var m = map[string]int{} m["route"] = 66 m["root"] = 67 for key,value := range m{ fmt.Println("Key:", key, "Value:", value) } }
因為map是無序的,如果想按照有序key輸出的話,可以先把所有的key取出,然后對key進行排序,再遍歷map,比如:
{ m := make(map[int]int) var keys []int for i := 0 ;i <= 5;i++{ m[i] = i } for k, v := range m{ fmt.Println("Key:",k,"Value:",v) } for k := range m{ keys = append(keys, k) } sort.Ints(keys) for _, k := range keys{ fmt.Println("Key:",k,"Value:",m[k]) } }
并發
字典不是并發安全的數據結構,如果某個任務正在對字典進行寫操作,那么其他任務就不能對該字典執行并發操作(讀、寫、刪除),否則會導致程序崩潰,比如:
m := make(map[string]int) go func(){ for { m["a"] += 1 time.Sleep(time.Microsecond) } }() go func (){ for{ _ = m["b"] time.Sleep(time.Microsecond) } }() select{} } 輸出: fatal error: concurrent map read and map write
GO語言編譯器提供了這種問題(競爭)的檢測方式,比如:
# go run -race file.go
安全
可以使用 sync.RWMutex 實現同步,避免并發環境多goroutings同時讀寫操作,繼續完善上面的例子,比如:
{ var lock = new(sync.RWMutex) m := make(map[string]int) go func(){ for { lock.Lock() m["a"]++ lock.Unlock() time.Sleep(time.Microsecond) } }() go func (){ for{ lock.RLock() _ = m["b"] lock.RUnlock() time.Sleep(time.Microsecond) } }() select{} }
性能
在創建字典時預先準備足夠的空間有助于提升性能,減少擴張時引發內存動態分配和重復哈希操作,比如:
package main import "testing" import "fmt" func test() map[int]int { m := make(map[int]int) for i:=0; i < 1000; i++{ m[i] = 1 } return m } func testCap() map[int]int{ m := make(map[int]int,1000) for i:=0; i < 1000; i++{ m[i] = 1 } return m } func BenchmarkTest(t *testing.B){ for i:= 0;i < t.N; i++{ test() } } func BenchmarkTestCap(t *testing.B){ for i:= 0;i < t.N; i++{ testCap() } } func main(){ resTest := testing.Benchmark(BenchmarkTest) fmt.Printf("BenchmarkTest \t %d, %d ns/op,%d allocs/op, %d B/op\n", resTest.N, resTest.NsPerOp(), resTest.AllocsPerOp(), resTest.AllocedBytesPerOp()) resTest = testing.Benchmark(BenchmarkTestCap) fmt.Printf("BenchmarkTestCap \t %d, %d ns/op,%d allocs/op, %d B/op\n", resTest.N, resTest.NsPerOp(), resTest.AllocsPerOp(), resTest.AllocedBytesPerOp()) } 輸出: # go run conmap.go BenchmarkTest 10000, 160203 ns/op,98 allocs/op, 89556 B/op BenchmarkTestCap 20000, 65478 ns/op,12 allocs/op, 41825 B/op
借鑒:<<雨痕筆記>>
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。