Go 语言 `map` 详解

Go 语言 map 详解

1. 什么是 map

在 Go 语言中,map 是一种 键值对(key-value) 数据结构,类似于 Python 的 dict 或 Java 的 HashMap。它提供了高效的查找、插入和删除操作。

2. map 的声明与初始化

在 Go 中,可以使用 make() 或直接字面量方式创建 map

方式 1:使用 make()
go 复制代码
m := make(map[string]int) // 创建一个键类型为 string,值类型为 int 的 map
方式 2:使用字面量
go 复制代码
m := map[string]int{
    "apple":  5,
    "banana": 10,
}
方式 3:创建空 map
go 复制代码
var m map[string]int // 此时 m 为空指针,不能直接赋值
m = make(map[string]int) // 需要初始化

3. 基本操作

添加或更新元素
go 复制代码
m["apple"] = 8
m["banana"] = 12
获取元素
go 复制代码
val := m["apple"]
fmt.Println(val) // 8
删除元素
go 复制代码
delete(m, "banana") // 删除 key 为 "banana" 的元素
检查 key 是否存在

map 访问时如果 key 不存在,返回零值,可以使用双返回值来判断:

go 复制代码
val, exists := m["banana"]
if exists {
    fmt.Println("banana 的值是", val)
} else {
    fmt.Println("banana 不存在")
}

4. 遍历 map

使用 for range 遍历 map

go 复制代码
for key, value := range m {
    fmt.Println("Key:", key, "Value:", value)
}

如果只需要 key

go 复制代码
for key := range m {
    fmt.Println("Key:", key)
}

5. map 不能作为函数参数传递值类型

在 Go 中,map 是引用类型,传递给函数时不会复制整个 map,而是传递引用。

go 复制代码
func modify(m map[string]int) {
    m["orange"] = 20
}

func main() {
    m := make(map[string]int)
    modify(m)
    fmt.Println(m) // map[orange:20]
}

6. map 的并发安全问题

map 不是线程安全的 ,多个 goroutine 并发写 map 时会导致 fatal error: concurrent map writes

解决方案:

  • 使用 sync.Mutex 保护 map

    go 复制代码
    var mu sync.Mutex
    m := make(map[string]int)
    
    go func() {
        mu.Lock()
        m["apple"] = 10
        mu.Unlock()
    }()
  • 使用 sync.Map (Go 1.9+)

    go 复制代码
    var m sync.Map
    m.Store("apple", 10)         // 写入
    val, ok := m.Load("apple")   // 读取
    fmt.Println(val, ok)

7. map 的长度与清空

获取 map 长度
go 复制代码
fmt.Println(len(m)) // 获取 map 中键值对的数量
清空 map

Go 没有直接清空 map 的方法 ,可以通过重新 make() 一个新 map

go 复制代码
m = make(map[string]int) // 清空 map

8. map 的键类型限制

mapkey 必须是可比较类型 ,即支持 ==!= 运算符,如:

✅ 允许:string, int, float, bool, struct

❌ 不允许:slice, map, function

示例:

go 复制代码
// 正确
m := map[int]string{1: "one", 2: "two"}

// 错误(切片不可作为 map key)
m := map[[]int]string{} // 编译错误

如果需要使用 slice 作为 key,可以转换为字符串:

go 复制代码
import "fmt"

func main() {
    m := make(map[string]int)
    key := fmt.Sprintf("%v", []int{1, 2, 3})
    m[key] = 10
    fmt.Println(m)
}

9. map 的值类型

map 的值类型可以是任何类型,包括 map 本身:

go 复制代码
nestedMap := map[string]map[string]int{
    "fruits": {
        "apple":  5,
        "banana": 8,
    },
}
fmt.Println(nestedMap["fruits"]["apple"]) // 5

10. map 的排序

map 默认是无序的,可以手动排序:

go 复制代码
import "sort"

keys := make([]string, 0, len(m))
for key := range m {
    keys = append(keys, key)
}
sort.Strings(keys) // 排序 keys

for _, key := range keys {
    fmt.Println("Key:", key, "Value:", m[key])
}

总结

  • map 是 Go 的键值对数据结构,查找、删除、插入都很高效(O(1))。
  • map 需要用 make() 或字面量方式初始化,未初始化的 map 不能写入数据。
  • 读取 map 时如果 key 不存在,会返回值类型的零值,判断存在性用 val, ok := m[key]
  • map 不是线程安全的,需要 sync.Mutexsync.Map 进行并发控制。
  • map 不能直接清空,只能 m = make(map[KeyType]ValueType)
  • map 的 key 只能是可比较类型,不能是 slice、map 或 function。
相关推荐
苏三说技术27 分钟前
Claude Code从失控到起飞,只用了这些技巧
后端
长栎1 小时前
写 for 循环写了十年,你却从没用过迭代器模式最狠的那一面
后端
LiaCode1 小时前
Redis 在生产项目的使用
前端·后端
用户559822481222 小时前
Docker Compose Down 导致容器数据误删——ext4 日志恢复全记录
后端
LiaCode2 小时前
一天学完 redis 的爽翻版核心知识总结
前端·后端
大刚测试开发实战2 小时前
如何内网穿透访问本地私有化部署的TestHub
前端·后端·github
xiaodaoluanzha2 小时前
迄今為止,最簡單的編程語言 Nolang
前端·后端
Csvn2 小时前
Docker 容器管理入门 — 从镜像到容器编排
后端
用户762352425912 小时前
ShardingJDBC
后端
行者全栈架构师2 小时前
IDEA 中 Maven 项目的 15 个红色报错快速解决方法
java·后端