☰
7.2 Go Map 进阶
2026/11/1 15:26:26 网站建设 项目流程

7.2 Go Map 进阶

1. Map 的值为切片 —map[K][]V

map 的值可以是切片,实现"一个键对应多个值"的数据结构:

packagemainimport"fmt"funcmain(){// map[string][]int — 学生名 → 成绩列表studentScores:=make(map[string][]int)// 直接赋值切片studentScores["Alice"]=[]int{92,88,95}studentScores["Bob"]=[]int{78,82,85}studentScores["Charlie"]=[]int{90,87,93}// 也可以 append 添加(nil 切片 append 安全)// studentScores["David"] = append(studentScores["David"], 80)fmt.Println("Student scores:",studentScores)}

执行结果:

Student scores: map[Alice:[92 88 95] Bob:[78 82 85] Charlie:[90 87 93]]

要点:

  • map[string][]int— 值类型是[]int切片,一个学生对应多个成绩
  • 赋值方式1:直接赋值切片studentScores["Alice"] = []int{92, 88, 95}
  • 赋值方式2:逐个 appendstudentScores["Alice"] = append(studentScores["Alice"], 92)
  • nil 切片 append 安全:studentScores["David"]为 nil,append 自动分配底层数组
  • 这是分组模式的基础:按键归类多个值

2. 嵌套 map — 安全访问两层键

嵌套 map 访问时必须逐层检查键是否存在,否则可能 panic:

packagemainimport"fmt"funcmain(){// 嵌套 map:科目 → 学生 → 成绩gradebook:=make(map[string]map[string]int)// 必须初始化内层 map!gradebook["Math"]=make(map[string]int)gradebook["Science"]=make(map[string]int)gradebook["Math"]["Alice"]=92gradebook["Math"]["Bob"]=85gradebook["Science"]["Alice"]=89gradebook["Science"]["Bob"]=88// 安全访问:两层 comma-ok 检查// 先检查 "Math" 是否存在,再检查 "Alice" 是否存在mathGrades,mathExists:=gradebook["Math"]ifmathExists{aliceMath,aliceExists:=mathGrades["Alice"]ifaliceExists{fmt.Printf("Alice's Math grade: %d\n",aliceMath)}}fmt.Println("Gradebook:",gradebook)}

执行结果:

Alice's Math grade: 92 Gradebook: map[Math:map[Alice:92 Bob:85] Science:map[Alice:89 Bob:88]]

要点:

  • make(map[string]map[string]int)— 外层科目,内层学生成绩
  • 必须初始化内层 map:gradebook["Math"] = make(map[string]int)
    • 不初始化:gradebook["Math"]["Alice"] = 92会 panic(内层是 nil map)
  • 两层 comma-ok 安全访问:
    • 第1层:mathGrades, mathExists := gradebook["Math"]
    • 第2层:aliceMath, aliceExists := mathGrades["Alice"]
  • 不安全访问的后果:
    • gradebook["English"]["Alice"]— “English” 不存在,返回 nil 内层 map,写入 panic
    • gradebook["Math"]["Unknown"]— “Unknown” 不存在,返回零值 0(不 panic,但可能误判)

3. Map 的值为结构体 — 修改需重新赋值

map 中结构体值是不可直接修改字段的,必须取出 → 修改 → 重新赋值:

packagemainimport"fmt"funcmain(){typePersonstruct{AgeintCitystring}people:=make(map[string]Person)people["John"]=Person{Age:30,City:"New York"}people["Jane"]=Person{Age:25,City:"Los Angeles"}// 错误方式:直接修改字段(编译报错!)// people["John"].Age = 31 // compile error: cannot assign to struct field in map// 正确方式:取出 → 修改 → 重新赋值john:=people["John"]// 取出结构体(值拷贝)john.Age=31// 修改拷贝people["John"]=john// 重新赋值回 mapfmt.Println("People:",people)}

执行结果:

People: map[Jane:{25 Los Angeles} John:{31 New York}]

要点:

  • people["John"].Age = 31—编译报错!Go 不允许直接修改 map 中结构体的字段
  • 原因:map 的值是不可寻址的(addressable)。people["John"]返回的是值的拷贝,不是引用
  • 正确步骤:
    1. john := people["John"]— 取出值拷贝
    2. john.Age = 31— 修改拷贝的字段
    3. people["John"] = john— 把修改后的值重新赋值回 map
  • 这个限制只针对值类型(struct、int 等),如果值是指针类型则可以直接修改:
    people:=make(map[string]*Person)people["John"]=&Person{Age:30,City:"New York"}people["John"].Age=31// 可以!指针可寻址
  • 也可以用指针 map 避免反复取出赋值:map[string]*Person

4. Map 模拟集合(Set)— 去重

用map[key]bool的键来模拟集合,实现去重:

packagemainimport"fmt"funcmain(){uniqueWords:=make(map[string]bool)words:=[]string{"hello","world","hello","go","world"}// 添加到集合:键存在即表示该词出现过for_,word:=rangewords{uniqueWords[word]=true}// 从集合获取所有元素varunique[]stringforword:=rangeuniqueWords{unique=append(unique,word)}fmt.Println("Unique words:",unique)}

执行结果:

Unique words: [hello world go]

要点:

  • uniqueWords[word] = true— 用 bool 值标记,true 表示元素在集合中
  • “hello” 和 “world” 出现两次,但 map 键唯一,所以只保留一个
  • 从集合提取元素:遍历 map 的键,for word := range uniqueWords
  • 集合特性:自动去重、快速判断存在(uniqueWords[word]返回 true/false)
  • 遍历顺序不确定:输出可能是[hello world go]或[go hello world]

5. Map 合并 — 后者覆盖前者

合并两个 map 时,重复键由后写入的值决定(覆盖):

packagemainimport"fmt"funcmain(){map1:=map[string]int{"a":1,"b":2}map2:=map[string]int{"b":3,"c":4}// "b" 在两个 map 中都存在merged:=make(map[string]int)// 先复制 map1fork,v:=rangemap1{merged[k]=v// merged: {a:1, b:2}}// 再复制 map2("b" 的值被覆盖)fork,v:=rangemap2{merged[k]=v// merged: {a:1, b:3, c:4} — b:2 被覆盖为 b:3}fmt.Println("Merged maps:",merged)}

执行结果:

Merged maps: map[a:1 b:3 c:4]

要点:

  • 合并方法:遍历两个 map,依次写入新 map
  • map1有b:2,map2有b:3,合并后merged["b"] = 3(map2 的值覆盖了 map1 的)
  • 覆盖规则:后写入的值覆盖先写入的值
  • 如果想保留 map1 的值优先,先复制 map2 再复制 map1:
    fork,v:=rangemap2{merged[k]=v}// map2 先fork,v:=rangemap1{merged[k]=v}// map1 覆盖 → b:2
  • Go 没有内置的 map 合并函数,需要手动遍历合并

知识点总结

知识点关键概念
map 值为切片map[K][]V— 一个键对应多个值,nil切片append安全
嵌套 mapmap[K1]map[K2]V— 必须逐层初始化内层,两层 comma-ok 安全访问
map 值为结构体不能直接修改字段,必须取出→修改→重新赋值;用指针可直接修改
map 模拟集合map[K]bool去重,键唯一自动去重
map 合并依次遍历写入新 map,后者覆盖前者(重复键由最后写入的值决定)

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询