映射(map)是Go语言内置的哈希表数据结构,用于存储键值对(key-value)。键必须是可比较的类型(如整数、字符串、浮点数、结构体等),值可以是任意类型。map提供O(1)平均时间复杂度的存取,在实际开发中使用极为广泛。
nil;对nil map进行读取不会报错,但写入会引发panic;使用make或字面量创建后才能写入。
var m map[string]int
fmt.Println(m == nil) // true
// m["key"] = 1 // panic: assignment to entry in nil map
// 不指定容量(自动扩容)
m1 := make(map[string]int)
// 指定初始容量(提高性能)
m2 := make(map[string]int, 100)
m1["age"] = 30
fmt.Println(m1["age"])
// 空map
m3 := map[string]int{}
// 带初始值
m4 := map[string]int{
"apple": 5,
"pear": 3,
"banana": 2,
}
fmt.Println(m4["apple"]) // 5
scores := map[string]int{}
scores["Alice"] = 95 // 插入
scores["Bob"] = 87
scores["Alice"] = 98 // 更新
fmt.Println(scores["Alice"]) // 98
// 不存在的键返回零值(不会panic)
fmt.Println(scores["Charlie"]) // 0
value, ok := scores["Charlie"]
if ok {
fmt.Println("存在,值为", value)
} else {
fmt.Println("键不存在")
}
delete(scores, "Bob") // 删除Bob
delete(scores, "Unknown") // 删除不存在的键不会报错
length := len(scores)
fmt.Println("当前map大小:", length)
使用for range遍历map,返回键和值。注意:遍历顺序是随机的(Go有意设计),不保证与插入顺序一致。
ages := map[string]int{
"Alice": 30,
"Bob": 25,
"Carol": 28,
}
for name, age := range ages {
fmt.Printf("%s 年龄 %d\n", name, age)
}
// 只遍历键
for name := range ages {
fmt.Println(name)
}
// 只遍历值
for _, age := range ages {
fmt.Println(age)
}
map作为函数参数传递时,传递的是指向底层数据结构的指针,函数内部对map的修改会影响外部。
func modify(m map[string]int) {
m["new"] = 100
}
func main() {
data := map[string]int{"old": 1}
modify(data)
fmt.Println(data) // map[new:100 old:1]
}
键类型必须支持==和!=操作。不能使用切片、map、函数作为键(这些类型不可比较)。但可以使用结构体、数组等作为键。
// 结构体作为键
type Point struct {
X, Y int
}
points := map[Point]string{
{0, 0}: "origin",
{1, 0}: "right",
}
fmt.Println(points[Point{1,0}]) // "right"
// 值可以是任意类型,包括切片、map等
var nested = map[string][]int{
"primes": {2,3,5,7},
"evens": {2,4,6,8},
}
Go原生的map不支持并发读写(会导致fatal error)。需要并发安全时,可以使用sync.Mutex保护map,或使用sync.Map(适用于读多写少或键值生命周期特殊的场景)。
import "sync"
func main() {
var sm sync.Map
// 存储
sm.Store("key1", 100)
// 读取
if val, ok := sm.Load("key1"); ok {
fmt.Println(val.(int)) // 类型断言
}
// 删除
sm.Delete("key1")
// 遍历
sm.Range(func(k, v interface{}) bool {
fmt.Println(k, v)
return true // 继续遍历
})
}
sync.RWMutex)才能在多个goroutine中安全使用。推荐使用sync.Map或自己加Mutex。
使用make(map[K]V, hint)预先分配容量可减少扩容次数,提高性能。当预先知道map可能存储的元素数量时,建议指定初始容量。
// 预计存储100个元素
m := make(map[string]int, 100)
package main
import (
"fmt"
"strings"
)
func main() {
text := "go is a great language. go is simple and efficient."
words := strings.Fields(strings.ToLower(text))
freq := make(map[string]int)
for _, w := range words {
// 去除标点(简单示例)
w = strings.Trim(w, ".,!?;")
freq[w]++
}
// 输出统计结果
for word, count := range freq {
fmt.Printf("%s: %d\n", word, count)
}
}