0.如何确定key是否存在? 如果访问了不存在的key会如何?
确定key是否存在,用ok判别式
1 2 3 4 5 if _,ok := m[key]; ok { print ("key存在" ) } else { print ("key不存在" ) }
参考:
ok判别式
在Go中操作map, 无论key是否存在,都不会panic或者返回error!
即可以访问不存在的key, 得到的值是对应 value 类型的零值
如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package mainimport "fmt" func main () { m := make (map [int ]int ) m[0 ] = 111 m[1 ] = 1313 m[2 ] = 9876 fmt.Println(m[4 ]) m2 := make (map [string ]string ) fmt.Println(m2["cuishuang" ]) var m3 map [string ]string fmt.Println(m3["dashen" ]) }
输出为:
简言之,可以判断(==)的类型,都可以作为map的键名.
如 bool, int,float,string, 指针, channel 以及 只包含前面几个类型的 interface,struct, array
而如 slice, map 以及 function 不能作为map的key,因为这几种类型无法用 == 来判断
原文如下:
As mentioned earlier(如前所述), map keys may be of any type that is comparable. The language spec defines this precisely, but in short, comparable types are boolean, numeric, string, pointer, channel, and interface types, and structs or arrays that contain only those types. Notably absent from the list are slices, maps, and functions; these types cannot be compared using ==, and may not be used as map keys.
comparable types , 即可比较类型,可判等类型
即slice,map,function不可比较,包含这三种类型的interface,struct,array也不可比较;除此以外的都可以比较~
关于类型是否能判等,可参考
利用反射,探究Go语言中的数据类型–哪些数据类型可以对比?
map之间能否直接判等? (直接 map1 == map2 是错误的)
结构体判等 (invalid operation: d1 == d2 (struct containing map[string]int cannot be compared))
接口的比较性 (如果接口存储的动态类型值是不可比较的,则运行时会报错)
切片的比较 (切片唯一合法的比较操作是和nil比较)
o := map[string]int{"a": 0, "b": 0, "c": 2}
如果原map的值不唯一(如上),会导致新map无法完全包含原map的键值对(相同key会被覆盖)。该问题可以采用多值map 来解决,即将新map的键值定义为一个切片类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var ( o = map [string ]int {"a" : 0 , "b" : 0 , "c" : 2 } ) func main () { fmt.Println(o) n := map [int ][]string {} for k, v := range o { n[v] = append (n[v], k) } fmt.Println(n) }
输出:
1 2 map[a:0 b:0 c:2] map[0:[a b] 2:[c]]
不可以!
map 中的元素并不是一个变量,而是一个值。 因而不能对 map 的元素进行取址操作(类似于&5,编译不过)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package mainimport ( "fmt" ) func main () { m := make (map [string ]int ) m["age" ] = 27 fmt.Println(&m["age" ]) }
报错:
1 cannot take the address of m["age" ]
4.当 map 的键值为结构体类型的值,那么无法直接修改结构体中的字段值
go 中的 map 的 value 本身是不可寻址的,因为 map 的扩容的时候,可能要做 key/val pair迁移
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 package mainimport "fmt" func main () { type person struct { name string city string age int } var personMap = make (map [int ]person) p5 := person{ name: "史铁生" , city: "北京" , age: 20 , } p1 := person{ "莫言" , "山东高密" , 30 , } personMap[p5.age] = p5 personMap[p1.age] = p1 fmt.Println("map为:" , personMap) fmt.Println("-----以下为正确修改方式-----" ) var personMap2 = make (map [int ]*person) personMap2[p5.age] = &p5 personMap2[p1.age] = &p1 fmt.Println(*personMap2[p1.age]) fmt.Println(*personMap2[p5.age]) fmt.Println("修改后:" ) personMap2[p1.age].name = "蒲松龄" fmt.Println(*personMap2[20 ]) fmt.Println(*personMap2[p1.age]) }
输出:
1 2 3 4 5 6 7 map为: map[20:{史铁生 北京 20} 30:{莫言 山东高密 30}] -----以下为正确修改方式----- {莫言 山东高密 30} {史铁生 北京 20} 修改后: {史铁生 北京 20} {蒲松龄 山东高密 30}
golang 修改map 结构体中的值
map增删改查
不能!
直接 map1 == map2 是错误的
map后面直接用 == 比较,只能用来判断 map 是否为 nil, 即只可以 “m == nil”
可以通过reflect.DeepEqual来”深度比较”两个slice/struct/map 是否相等
参考:
如何比较两个 map 相等
比较两个 slice/struct/map 是否相等
借助slice就可以
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 package mainimport ( "fmt" "sort" ) func main () { var m = make (map [int ]string ) m[1 ] = "张三" m[2 ] = "李四" m[3 ] = "王五" m[4 ] = "陈六子" fmt.Println() fmt.Println("下面迭代时会乱序:" ) fmt.Println() for k, v := range m { fmt.Printf("第%d位是%s\n" , k, v) } fmt.Println("---------------顺序输出----------------" ) sli := make ([]int , 0 ) for k := range m { sli = append (sli, k) } sort.Ints(sli) for _, val := range sli { fmt.Printf("第%d位是%s\n" , val, m[val]) } }
深入探究map迭代时的乱序
统计某月有几篇博文
并发读写map可能会报错(不是100%),而且该错误无法通过recover恢复(并发对map读写可能会报fatal error)
并发读写slice不会报错(最后的数据可能不对)
更多参考:
golang之map并发访问
以下是 Go 中常见的各种 map 操作的时间复杂度:
查找元素(Get):平均时间复杂度为 O(1),最坏情况下为 O(n)。
插入元素(Put):平均时间复杂度为 O(1) ,最坏情况下为 O(n)。
删除元素(Delete):平均时间复杂度为 O(1),最坏情况下为 O(n)。
遍历元素(Iteration):时间复杂度为 O(n),其中 n 为 map 的大小。
需要注意的是,由于 map 的实现方式和哈希表类似,因此查找、插入、删除等操作的时间复杂度是平均意义下的复杂度,具体的时间复杂度会受到 hash 算法、哈希冲突等因素的影响,最坏情况下可能会退化到 O(n)。
20251011 补充:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 package mainimport ( "fmt" "maps" ) var globalMap map [string ]int func CopyMap () map [string ]int { var result map [string ]int for k, v := range globalMap { result[k] = v } return result } func CloneMap () map [string ]int { return maps.Clone(globalMap) } func main () { if globalMap == nil { fmt.Println(111 ) } fmt.Println(CopyMap() == nil ) fmt.Println(CloneMap() == nil ) }
1. var xxxx 声明的map 如果没有初始化(即var这行表达式没有=),是 nil
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package mainvar globalMap map [string ]int type Package struct {}var gPackages map [string ]*Packagevar gResult = map [string ]int {"test" : 123 }func main () { result := make (map [string ]int ) println (globalMap == nil ) println (gPackages == nil ) println (gResult == nil ) println (result == nil ) }
2. 对 nil map range 是安全的,不会 panic; 但对nil map赋值,则会panic 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package mainvar globalMap map [string ]int func main () { var result map [string ]int for k, v := range globalMap { println (111 ) result[k] = v } for k, v := range result { println (222 ) globalMap[k] = v } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package mainvar globalMap map [string ]int var gResult = map [string ]int {"test" : 123 }func main () { var result map [string ]int for k, v := range gResult { println (111 ) println (k) result[k] = v } for k, v := range gResult { println (222 ) println (k) globalMap[k] = v } }
1 2 3 4 5 6 7 8 9 111 test panic: assignment to entry in nil map goroutine 1 [running]: main.main() /Users/mac/find_tools_bug/modernize/mapsloop/mapsclone/shuang-test/main.go:14 +0xb0 exit status 2
3. maps.Copy 和 maps.Clone 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package mainimport ( "fmt" "maps" ) var globalMap map [string ]int func CopyMap () map [string ]int { var result map [string ]int maps.Copy(result, globalMap) return result } func CloneMap () map [string ]int { return maps.Clone(globalMap) } func main () { if globalMap != nil { fmt.Println(111 ) } fmt.Println(CopyMap()) fmt.Println(CloneMap()) fmt.Println(CopyMap() == nil ) fmt.Println(CloneMap() == nil ) }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package mainimport ( "fmt" "maps" ) var globalMap map [string ]int = map [string ]int {"123" : 1 }func CopyMap () map [string ]int { var result map [string ]int maps.Copy(result, globalMap) return result } func CloneMap () map [string ]int { return maps.Clone(globalMap) } func main () { if globalMap != nil { fmt.Println(111 ) } fmt.Println(CopyMap()) fmt.Println(CloneMap()) fmt.Println(CopyMap() == nil ) fmt.Println(CloneMap() == nil ) }
这样会panic,因为maps.Copy相当于
1 2 3 4 5 var result map [string ]int for k, v := range globalMap { result[k] = v }
而result此时为nil
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package mainimport ( "fmt" "maps" ) var globalMap map [string ]int = map [string ]int {"123" : 1 }func CopyMap () map [string ]int { result := make (map [string ]int ) maps.Copy(result, globalMap) return result } func CloneMap () map [string ]int { return maps.Clone(globalMap) } func main () { if globalMap != nil { fmt.Println(111 ) } fmt.Println(CopyMap()) fmt.Println(CloneMap()) fmt.Println(CopyMap() == nil ) fmt.Println(CloneMap() == nil ) }
1 2 3 4 5 111 map[123:1] map[123:1] false false
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package mainimport ( "fmt" "maps" ) var globalMap map [string ]int func CopyMap () map [string ]int { result := make (map [string ]int ) maps.Copy(result, globalMap) return result } func CloneMap () map [string ]int { return maps.Clone(globalMap) } func main () { if globalMap != nil { fmt.Println(111 ) } fmt.Println(CopyMap()) fmt.Println(CloneMap()) fmt.Println(CopyMap() == nil ) fmt.Println(CloneMap() == nil ) }
此时就有问题了,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 package mainimport ( "fmt" "maps" ) var globalMap map [string ]int func CopyMap () map [string ]int { result := make (map [string ]int ) maps.Copy(result, globalMap) return result } func CloneMap () map [string ]int { return maps.Clone(globalMap) } func raw () map [string ]int { result := make (map [string ]int ) for k, v := range globalMap { result[k] = v } return result } func main () { if globalMap != nil { fmt.Println(111 ) } fmt.Println(CopyMap()) fmt.Println(CloneMap()) fmt.Println(CopyMap() == nil ) fmt.Println(CloneMap() == nil ) newMap := raw() newMap["world" ] = 678 newMap2 := CloneMap() newMap2["hello" ] = 789 }
1 2 3 4 5 6 7 8 9 10 map[] map[] false true panic: assignment to entry in nil map goroutine 1 [running]: main.main() /Users/mac/find_tools_bug/modernize/mapsloop/mapsclone/shuang-test/main.go:46 +0x380 exit status 2
和以下类似
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package mainimport ( "fmt" "maps" ) type Package struct {}func main () { var gPackages map [string ]*Package = nil pkgs := make (map [string ]*Package, len (gPackages)) for id, mp := range gPackages { pkgs[id] = mp } fmt.Println("pkgs == nil?" , pkgs == nil ) pkgs["x" ] = &Package{} fmt.Println("ok" ) }
也就是说,当src参数为nil时, 要特别小心
原文链接: https://dashen.tech/2017/12/06/golang之map入门/
版权声明: 转载请注明出处.