一、map 是什么?
map 是 Go 里 键值对(key-value) 的集合,类似:
-
Java 的
HashMap -
Python 的
dict
Go
map[KeyType]ValueType
特点:
无序、引用类型、key 唯一、查找快
二、map 的基本使用
1. 声明 & 初始化
Go
// 声明但未初始化(nil map)
var m map[string]int
// 初始化(推荐)
m = make(map[string]int)
// 声明 + 初始化
m := make(map[string]int)
// 带初始值
m := map[string]int{
"java": 8,
"go": 3,
"python": 5,
}
注意:下面的写法会报错:
Go
var m map[string]int
m["a"] = 1 // ❌ panic:assignment to entry in nil map
map 用之前 必须 make 或字面量初始化
2. map的读 / 写 / 删
Go
m["go"] = 10 // 写
v := m["go"] // 读
delete(m, "redis") // 删除
3. 判断 key 是否存在
Go
v, ok := m["python"]
if ok {
fmt.Println("存在", v)
} else {
fmt.Println("不存在")
}
如果 key 不存在:
-
v是 value 类型的零值 -
ok == false
三、map 的遍历
Go
for k, v := range m {
fmt.Println(k, v)
}
注意
-
遍历顺序 每次都可能不一样
-
Go 故意打乱顺序,避免依赖顺序写代码
如果需要顺序:
单独维护一个 slice 存 key
四、map 的 key / value 有哪些限制?
1. key 必须是 可比较类型
以下可以用作key:
-
int -
string -
bool -
指针
-
数组
-
struct(前提:字段都可比较)
以下 不可以用作key:
-
slice -
map -
function
2 value 没限制
Go
map[string][]int
map[string]map[string]int
map[string]interface{}
五、map 是引用类型
Go
func modify(m map[string]int) {
m["go"] = 100
}
func main() {
m := map[string]int{"go": 10}
modify(m)
fmt.Println(m["go"]) // 100
}
将map作为参数传入map,vaule不会发生改变,是因为:
-
map底层是 指针结构 -
函数传参不会拷贝整个 map
六、并发安全问题
map 不是并发安全的,使用不当会报以下错误:
fatal error: concurrent map writes
可以使用以下方式来实现并发安全
1.加锁(最常用)
Go
var m = make(map[string]int)
var mu sync.Mutex
mu.Lock()
m["a"] = 1
mu.Unlock()
- 使用
sync.Map(读多写少)
Go
var m sync.Map
m.Store("a", 1)
v, ok := m.Load("a")
3.channel 串行化访问
七、map 的一些常见坑
- 不能直接取地址
Go
v := m["go"]
// &m["go"] 不允许
因为 map 在扩容时,地址会变化
- 不能比较 map
Go
m1 == m2 // 编译错误
只能和 nil 比较:
Go
if m == nil {}
3. map 扩容会导致性能抖动
可以提前分配容量:
Go
m := make(map[string]int, 10000)
八、和 Java HashMap 的对比
| 维度 | Go map | Java HashMap |
|---|---|---|
| 是否有序 | ❌ 无序 | ❌ 无序 |
| 是否线程安全 | ❌ | ❌ |
| 初始化 | make / 字面量 | new HashMap |
| 判断存在 | v, ok := m[k] |
containsKey() |
| nil 行为 | 写会 panic | put 会 NPE |
| 泛型 | 原生支持 | Java 8+ |
九、典型使用场景
-
缓存 / 去重
-
计数器(词频统计)
-
ID → 对象 映射
-
配置表
-
JSON 反序列化动态字段
Go
count := make(map[string]int)
for _, w := range words {
count[w]++
}
十、总结
Go 的 map = 高性能、语法极简,但对并发和顺序非常"严格"的 HashMap