Skip to content

映射(Map)

Map是键值对的无序集合。

创建Map

使用make创建

go
// 创建空map
ages := make(map[string]int)

// 添加元素
ages["张三"] = 25
ages["李四"] = 30

使用字面量创建

go
scores := map[string]int{
    "数学": 95,
    "语文": 87,
    "英语": 92,
}

创建空map

go
// 空map,可以直接使用
m1 := make(map[string]int)

// nil map,需要初始化后才能使用
var m2 map[string]int
m2 = make(map[string]int)  // 必须初始化

Map操作

访问元素

go
scores := map[string]int{
    "数学": 95,
    "语文": 87,
}

// 访问元素
mathScore := scores["数学"]
fmt.Println("数学分数:", mathScore)  // 95

// 访问不存在的键,返回零值
physicsScore := scores["物理"]
fmt.Println("物理分数:", physicsScore)  // 0

检查键是否存在

go
scores := map[string]int{"数学": 95}

// 使用双返回值检查
if score, exists := scores["物理"]; exists {
    fmt.Println("物理分数:", score)
} else {
    fmt.Println("没有找到物理分数")
}

修改元素

go
scores := map[string]int{"数学": 95}

// 修改
scores["数学"] = 100

// 添加
scores["物理"] = 88

删除元素

go
scores := map[string]int{
    "数学": 95,
    "语文": 87,
}

delete(scores, "语文")  // 删除键"语文"

获取长度

go
scores := map[string]int{"数学": 95, "语文": 87}
fmt.Println(len(scores))  // 2

遍历Map

go
scores := map[string]int{
    "数学": 95,
    "语文": 87,
    "英语": 92,
}

// 遍历键值对
for subject, score := range scores {
    fmt.Printf("%s: %d\n", subject, score)
}

// 只遍历键
for subject := range scores {
    fmt.Println("科目:", subject)
}

注意

Map的遍历顺序是随机的,每次遍历结果可能不同。

Map示例

完整示例

go
package main

import "fmt"

func main() {
    // 创建学生成绩map
    scores := make(map[string]map[string]int)
    
    // 添加学生成绩
    scores["张三"] = map[string]int{
        "数学": 95,
        "语文": 87,
        "英语": 92,
    }
    
    scores["李四"] = map[string]int{
        "数学": 88,
        "语文": 90,
        "英语": 85,
    }
    
    // 遍历所有学生成绩
    for name, subjects := range scores {
        fmt.Printf("\n%s的成绩:\n", name)
        total := 0
        for subject, score := range subjects {
            fmt.Printf("  %s: %d\n", subject, score)
            total += score
        }
        fmt.Printf("  平均分: %.1f\n", float64(total)/float64(len(subjects)))
    }
}

统计单词出现次数

go
func wordCount(text string) map[string]int {
    words := strings.Fields(text)
    counts := make(map[string]int)
    
    for _, word := range words {
        counts[word]++
    }
    return counts
}

func main() {
    text := "hello world hello go world world"
    counts := wordCount(text)
    
    for word, count := range counts {
        fmt.Printf("%s: %d\n", word, count)
    }
    // 输出:
    // hello: 2
    // world: 3
    // go: 1
}

分组

go
func groupBy(items []string, keyFunc func(string) string) map[string][]string {
    groups := make(map[string][]string)
    
    for _, item := range items {
        key := keyFunc(item)
        groups[key] = append(groups[key], item)
    }
    return groups
}

func main() {
    words := []string{"apple", "banana", "avocado", "blueberry", "cherry"}
    
    // 按首字母分组
    groups := groupBy(words, func(s string) string {
        return string(s[0])
    })
    
    for letter, items := range groups {
        fmt.Printf("%s: %v\n", letter, items)
    }
    // 输出:
    // a: [apple avocado]
    // b: [banana blueberry]
    // c: [cherry]
}

Map的键类型

Map的键必须是可比较的类型:

支持的类型

  • 基本类型:int、float、string、bool
  • 指针
  • 数组
  • 结构体(字段都可比较)
  • 接口类型

不支持的类型

  • 切片
  • Map
  • 函数
go
// 正确
m1 := make(map[int]string)
m2 := make(map[string]int)
m3 := make(map[[3]int]string)

// 错误
// m4 := make(map[[]int]string)  // 切片不能作为键

并发安全

Map不是并发安全的,在并发环境下需要加锁:

go
import "sync"

type SafeMap struct {
    mu sync.RWMutex
    m  map[string]int
}

func (sm *SafeMap) Get(key string) (int, bool) {
    sm.mu.RLock()
    defer sm.mu.RUnlock()
    val, ok := sm.m[key]
    return val, ok
}

func (sm *SafeMap) Set(key string, value int) {
    sm.mu.Lock()
    defer sm.mu.Unlock()
    sm.m[key] = value
}

或者使用 sync.Map

go
var m sync.Map

// 存储
m.Store("key", "value")

// 读取
if val, ok := m.Load("key"); ok {
    fmt.Println(val)
}

// 删除
m.Delete("key")

// 遍历
m.Range(func(key, value interface{}) bool {
    fmt.Printf("%v: %v\n", key, value)
    return true
})