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。
相关推荐
枫叶丹433 分钟前
【Qt开发】信号与槽(二)-> 信号和槽的使用
开发语言·qt
小鱼人爱编程35 分钟前
Java基石--反射让你直捣黄龙
前端·spring boot·后端
hqxstudying40 分钟前
J2EE模式---服务层模式
java·数据库·后端·spring·oracle·java-ee
GM_8281 小时前
【最新最完整】SpringAI-1.0.0开发MCP Server,搭建MCP Client 实战笔记(进阶+详细+完整代码)
java·后端·ai编程·springai·mcp
程序员爱钓鱼1 小时前
Go语言实战案例-滑动窗口最大值
后端·google·go
Vertira1 小时前
python 阿里云 安装 dashscope的简介、安装
开发语言·python
Victor3562 小时前
MySQL(163) 如何理解MySQL的隔离级别?
后端
Victor3562 小时前
MySQL(164)如何设置MySQL的隔离级别?
后端
hqxstudying3 小时前
Java异常处理
java·开发语言·安全·异常
代码老y3 小时前
ASP.NET Core 高并发万字攻防战:架构设计、性能优化与生产实践
后端·性能优化·asp.net