go——字典
Go中字典类型是散列表(hash table)的一个实现,其官方称谓是map。
散列表是一个实现了关联数组的数据结构,关联数组是用于表示键值对的无序集合的一种抽象数据类型。
Go中称键值对为键-元素对,它把字典中的每个键都看作与其对应的元素的索引,这样的索引再同一个字典值中是唯一的。
下面的代码声明了一个字典类型的变量:
1 | var ipSwitches = map [string]bool{} |
变量ipSwitches的键类型为string,元素类型为bool。
map[string]bool{}表示了一个不包含任何元素的字典值。
与切片类型一样,字典类型是一个引用类型。也正因此,字典类型的零值是nil。
字典值的长度表示了其中的键-元素对的数量,其零值的长度总是0.
将其作为语言内置类型,从运行时层面进行优化,可获得更高效的性能。
作为无序键值对集合,字典要求key必须是支持相等运算符(==、!=)的数据类型。
字典是引用类型,使用make函数或初始化表达式来创建。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | package main import "fmt" func main() { m := make( map [string]int) //make函数创建 m[ "a" ] = 1 m[ "b" ] = 2 m2 := map [int] struct { //key的类型int,value的类型struct x int }{ 1: {x: 100}, 2: {x: 200}, } fmt.Println(m, m2) //map[a:1 b:2] map[1:{100} 2:{200}] } |
字典的基本操作:增删改查
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | package main import "fmt" func main() { m := map [string]int{ "a" : 1, "b" : 2, } m[ "a" ] = 10 //修改 m[ "c" ] = 30 //添加 fmt.Println(m) //map[a:10 b:2 c:30] if v, ok := m[ "d" ]; ok { //使用ok-idiom判断键是否存在 fmt.Println(v) } delete(m, "b" ) //删除一个键值对 fmt.Println(m) //map[a:10 b:2 c:30] fmt.Println(m[ "f" ]) //0 访问不存在的键值对时返回0值 } |
对字典进行迭代每次返回的键值次序都不相同
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 main import "fmt" func main() { m := make( map [string]int) for i := 0; i < 7; i++ { m[string( 'a' +i)] = i } for i := 0; i < 4; i++ { for k, v := range m { fmt.Printf( "%s:%d " , k, v) } fmt.Println() } } /* g:6 a:0 b:1 c:2 d:3 e:4 f:5 e:4 f:5 g:6 a:0 b:1 c:2 d:3 g:6 a:0 b:1 c:2 d:3 e:4 f:5 a:0 b:1 c:2 d:3 e:4 f:5 g:6 */ |
函数len返回当前键值对的数量,cap不接受字典类型.
因内存访问安全和哈希算法等缘故,字典被设计成"not addressable",
因此不能直接修改value成员(结构或数组)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | package main import "fmt" func main() { type user struct { name string age int } m := map [int]user{ 1: { "kebi" , 19}, } fmt.Println(len(m)) //1 fmt.Println(cap(m)) //invalid argument m (type map[int]user) for cap m[1].age += 1 // cannot assign to struct field m[1].age in map } |
正确的做法是返回整个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 main import "fmt" func main() { type user struct { name string age int } m := map [int]user{ 1: { "kebi" , 19}, } u := m[1] //取出整个value值 u.age += 1 //现在操作的对象是结构体 m[1] = u //重新赋值 fmt.Println(m) m2 := map [int]*user{ //value是指针类型 1: &user{ "Jack" , 20}, } m2[1].age++ //m2[1]返回的是指针,透过指针修改目标对象 fmt.Println(m2[1]) } |
不能对nil字典进行赋值操作,但却能读。
1 2 3 4 5 6 7 8 9 10 11 12 | package main import "fmt" func main() { var m map [string]int //只进行声明的字典是nil字典 fmt.Println(m[ "a" ]) //0 // m["a"] = 1 //panic: assignment to entry in nil map m2 := map [string]int{} fmt.Println(m == nil, m2 == nil) //true 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 | package main import "fmt" func main() { m := make( map [int]int) for i := 0; i < 10; i++ { m[i] = i + 10 } for k := range m { if k == 5 { m[100] = 1000 } delete(m, k) fmt.Println(k, m) } } /* 0 map[9:19 2:12 6:16 8:18 7:17 1:11 3:13 4:14 5:15] 2 map[5:15 7:17 1:11 3:13 4:14 8:18 9:19 6:16] 6 map[4:14 5:15 7:17 1:11 3:13 8:18 9:19] 8 map[7:17 1:11 3:13 4:14 5:15 9:19] 9 map[4:14 5:15 7:17 1:11 3:13] 1 map[3:13 4:14 5:15 7:17] 3 map[4:14 5:15 7:17] 4 map[5:15 7:17] 5 map[7:17 100:1000] 7 map[100:1000] */ |
运行时会对字典并发操作做出检测.如果某个任务正在对字典进行写操作,
那么其它任务就不能对该字典执行并发操作(读写删除),否则会导致进程崩溃.
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 main import "time" func main() { 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 |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理