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。
相关推荐
穆韵澜16 分钟前
SQL语言的云计算
开发语言·后端·golang
nqqcat~35 分钟前
函数的引用/函数的默认参数/函数的占位参数/函数重载
开发语言·c++·算法
getapi39 分钟前
cursor全栈网页开发最合适的技术架构和开发语言
开发语言·架构
uhakadotcom1 小时前
提升PyODPS性能的实用技巧
后端·面试·github
字节源流1 小时前
【SpringMVC】入门版
java·后端
daily_23331 小时前
c++领域展开第十六幕——STL(vector容器的了解以及各种函数的使用)超详细!!!!
开发语言·c++·vector·visual studio code
努力学习的小廉1 小时前
【C++】 —— 笔试刷题day_5
开发语言·c++
MrWho不迷糊1 小时前
Spring Boot 的优雅启停:确保停机不影响交易
spring boot·后端
xjz18421 小时前
Netty底层原理深度解析:高并发网络编程的核心设计
后端
观无1 小时前
C#的简单工厂模式、工厂方法模式、抽象工厂模式
java·开发语言·c#