引入

切片用数字索引存取数据,但很多时候需要用名字、ID 这类自定义键来查找数据。Go 的 map 就是用来做这件事的,用键值对保存数据,通过键快速查找值。

正文

定义

定义

map 用键值对保存数据,通过键快速查找值。键类型必须是可比较的(数字、字符串、布尔、数组、结构体等),值类型不限。map 是引用类型,零值是 nil,未初始化的 map 不能写入。

语法

// 字面量创建
m := map[string]int{
    "语文": 90,
    "数学": 95,
}
 
// make 创建
m := make(map[string]int)

例子

创建和赋值

m := make(map[string]int)
m["语文"] = 90
m["数学"] = 95
 
fmt.Println(m) // map[数学:95 语文:90]

make() 初始化后才能写入,字面量创建则不需要 make()

读取值

m := map[string]int{"a": 1, "b": 2}
 
fmt.Println(m["a"]) // 1
fmt.Println(m["c"]) // 0(键不存在,返回零值)

键不存在时返回值类型的零值,不会报错。

判断键是否存在

m := map[string]int{"a": 1}
 
v, ok := m["a"]
fmt.Println(v, ok) // 1 true
 
v, ok = m["b"]
fmt.Println(v, ok) // 0 false

v, ok := m[key] 的逗号 ok 模式判断键是否存在,oktrue 表示存在。

修改值

m := map[string]int{"a": 1}
m["a"] = 100
fmt.Println(m["a"]) // 100

对已有的键重新赋值就是修改。

删除键值对

m := map[string]int{"a": 1, "b": 2}
delete(m, "a")
 
fmt.Println(m)       // map[b:2]
fmt.Println(len(m))  // 1

delete(m, key) 删除指定键,键不存在也不会报错。

遍历 map

m := map[string]int{
    "语文": 90,
    "数学": 95,
    "英语": 88,
}
 
for k, v := range m {
    fmt.Println(k, v)
}

for range 遍历 map,顺序不固定,每次运行可能不同。

map 是引用类型

m1 := map[string]int{"a": 1}
m2 := m1
m2["a"] = 100
 
fmt.Println(m1["a"]) // 100(跟着变了)

map 赋值或传参共享底层数据,修改会互相影响。

nil map

var m map[string]int
fmt.Println(m == nil) // true
// m["a"] = 1         // panic: assignment to entry in nil map

nil map 可以读取(返回零值),但不能写入,写入会 panic。

map 嵌套

scores := map[string]map[string]int{
    "Tom": {"语文": 90, "数学": 85},
    "Amy": {"语文": 88, "数学": 92},
}
 
fmt.Println(scores["Tom"]["语文"]) // 90

map 的值可以是另一个 map,实现嵌套结构。

用结构体做值

type Student struct {
    Name  string
    Score int
}
 
m := map[string]Student{
    "001": {Name: "Tom", Score: 90},
    "002": {Name: "Amy", Score: 85},
}
 
fmt.Println(m["001"].Name) // Tom

map 的值可以是任意类型,包括结构体。

常见写法

写法说明
m := map[K]V{…}字面量创建
m := make(map[K]V)make() 创建
m[key] = v新增/修改
v := m[key]读取(不存在返回零值)
v, ok := m[key]读取并判断是否存在
delete(m, key)删除键值对
len(m)获取键值对数量
for k, v := range m遍历

特点

  • map 用键值对保存数据,通过键快速查找
  • 键类型必须可比较,切片、map、函数不能做键
  • 键不存在时返回值类型的零值,用逗号 ok 模式判断是否存在
  • delete() 删除键值对,键不存在不报错
  • for range 遍历顺序不固定
  • map 是引用类型,赋值和传参共享数据
  • nil map 不能写入,必须先 make() 或字面量初始化
  • map 不是并发安全的,并发读写需要加锁或用 sync.Map

理解

map 就像一个字典,给每个数据贴一个标签(键),通过标签就能快速找到数据。用起来很直觉:创建、存、取、删,四个操作就能覆盖大部分场景。要注意的是 map 是引用类型,赋值后修改会互相影响,还有并发读写要加锁。

引出

map 能用键存取数据,但如果想把不同类型的数据组合成一个整体,接下来看 Go 结构体,学习怎么把相关数据打包在一起。