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
:govar mu sync.Mutex m := make(map[string]int) go func() { mu.Lock() m["apple"] = 10 mu.Unlock() }()
-
使用
sync.Map
(Go 1.9+)govar 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
的键类型限制
map
的 key 必须是可比较类型 ,即支持 ==
或 !=
运算符,如:
✅ 允许: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.Mutex
或sync.Map
进行并发控制。map
不能直接清空,只能m = make(map[KeyType]ValueType)
。map
的 key 只能是可比较类型,不能是 slice、map 或 function。