第十九回:Go 语言 Map(集合)

华府的花名册(键值对 Map)
唐伯虎
唐伯虎: 数组和切片都是按顺序排队的,找人太慢。我们需要一本花名册,只要喊名字(Key),立马就能找到人(Value)。这就是 Map (映射/字典)

1. 📖 花名册 (Map 定义与初始化)

Map 是无序的 Key (键) - Value (值) 对。Key 必须是能比较相等的数据类型(如 string, int),不能是 slice 或 map。

// 方式一:使用 make (推荐)
// 只有 make 过的 map 才能存东西,否则是 nil map,写就崩!
var ages map[string]int
ages = make(map[string]int) 
ages["唐伯虎"] = 25

// 方式二:字面量初始化
roles := map[string]string{
    "9527": "低级家丁",
    "华安": "高级伴读书童", // 注意:最后一行也要有逗号
}

2. ⚠️ 空指针陷阱 (Nil Map)

华夫人
华夫人: 记住了!只定义不初始化的 Map 是个空壳子 (nil)。你可以读它(返回零值),但往里面写东西会直接崩溃 (Panic)
var m map[string]int // m is nil
// m["key"] = 1 // ❌ 报错:assignment to entry in nil map

m = make(map[string]int) // ✅ 初始化后就可以写了
m["key"] = 1 // OK

3. 🔍 查户口 (增删改查)

Map 的查找非常快,时间复杂度是 O(1)。读取不存在的 Key 会返回 Value 类型的零值。

// 1. 查找 (Comma-ok 模式)
role, exists := roles["祝枝山"] // exists 是个布尔值
if exists {
    fmt.Println("祝枝山是", role)
} else {
    fmt.Println("查无此人")
}

// 2. 直接读取(如果不存在,返回空字符串)
fmt.Println(roles["不存在的人"]) // 输出空字符串

// 3. 删除
delete(roles, "9527") // 即使 key 不存在也不会报错

// 4. 长度
fmt.Println(len(roles))

4. 🔄 遍历 (Range)

遍历 Map 的顺序是随机的!每次运行可能都不一样。不要依赖遍历顺序。

for key, value := range roles {
    fmt.Printf("%s -> %s\n", key, value)
}
⚠️ 高级陷阱:并发读写

Go 原生 Map 不是线程安全的!如果多个 goroutine 同时读写同一个 Map,程序会直接崩溃(fatal error: concurrent map writes)。并发场景请使用 sync.Map

🎯 练功房(除名)

把 "9527" 从花名册里删掉。

package main
import "fmt"

func main() {
    m := map[string]string{"9527": "家丁", "秋香": "丫鬟"}
    
    // 填空:删除 "9527"
    delete(m, "______")
    
    fmt.Println(m)
}

任务: 填空处应该写什么?

答案: 9527

代码: delete(m, "9527")