Golang Map 深度解析
基于 Go 1.26.4 源码,源码路径:
/home/lin/src/github.com/go-go1.26.4核心源文件:
internal/runtime/maps/map.go、internal/runtime/maps/group.go、internal/runtime/maps/table.go、runtime/map.go
1 map 功能完整介绍
1.1 语法定义
Go 内置哈希表 map 使用 map[KeyType]ValueType 语法定义:
go
// 声明(零值 nil,不可直接赋值)
var m map[string]int
// make 初始化(指定初始容量可选)
m = make(map[string]int)
m = make(map[string]int, 100) // hint=100
// 字面量初始化
m := map[string]int{
"alice": 90,
"bob": 85,
}
// 空字面量(非 nil!)
m := map[string]int{}
1.2 键值类型约束
key 类型必须满足 comparable 约束 ------即支持 == 和 != 比较:
| 可用 key 类型 | 不可用 key 类型 | 原因 |
|---|---|---|
int, int8...int64 |
[]T(切片) |
切片不可比较 |
uint, uint8...uint64 |
map[K]V(map) |
map 不可比较 |
float32, float64 |
func(函数) |
函数不可比较 |
string |
||
bool |
||
complex64, complex128 |
||
指针 *T |
指针比较的是地址 | |
数组 [N]T(元素可比较) |
||
结构体 struct{...}(字段均可比较) |
||
接口 interface{} |
接口比较动态类型+值 |
value 类型无限制------可以是任意类型,包括切片、map、函数、通道等。
1.3 存储特性
| 特性 | 说明 |
|---|---|
| 无序 | for range 遍历顺序随机,每次运行可能不同 |
| 引用类型 | map 变量本质是指针,传参不拷贝数据 |
| 非并发安全 | 同时读写会触发 fatal: concurrent map read and map write |
| 动态扩容 | 负载因子超过 7/8 时触发扩容 |
| 不可取址 | &m["key"] 编译错误,因为扩容会使地址失效 |
1.4 适用场景
#mermaid-svg-6p4OZQ9CYaPVe7I0{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-6p4OZQ9CYaPVe7I0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .error-icon{fill:#552222;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .marker.cross{stroke:#333333;}#mermaid-svg-6p4OZQ9CYaPVe7I0 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6p4OZQ9CYaPVe7I0 p{margin:0;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .edge{stroke-width:3;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section--1 rect,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section--1 path,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section--1 circle,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section--1 polygon,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section--1 text{fill:#ffffff;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-6p4OZQ9CYaPVe7I0 .edge-depth--1{stroke-width:17;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled,#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled circle,#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled text{fill:lightgray;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled text{fill:#efefef;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-0 rect,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-0 path,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-0 circle,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-0 polygon,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-0 text{fill:black;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .node-icon-0{font-size:40px;color:black;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-6p4OZQ9CYaPVe7I0 .edge-depth-0{stroke-width:14;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled,#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled circle,#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled text{fill:lightgray;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled text{fill:#efefef;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-1 rect,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-1 path,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-1 circle,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-1 polygon,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-1 text{fill:black;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .node-icon-1{font-size:40px;color:black;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-6p4OZQ9CYaPVe7I0 .edge-depth-1{stroke-width:11;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled,#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled circle,#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled text{fill:lightgray;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled text{fill:#efefef;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-2 rect,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-2 path,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-2 circle,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-2 polygon,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-2 text{fill:#ffffff;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-6p4OZQ9CYaPVe7I0 .edge-depth-2{stroke-width:8;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled,#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled circle,#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled text{fill:lightgray;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled text{fill:#efefef;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-3 rect,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-3 path,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-3 circle,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-3 polygon,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-3 text{fill:black;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .node-icon-3{font-size:40px;color:black;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-6p4OZQ9CYaPVe7I0 .edge-depth-3{stroke-width:5;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled,#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled circle,#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled text{fill:lightgray;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled text{fill:#efefef;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-4 rect,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-4 path,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-4 circle,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-4 polygon,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-4 text{fill:black;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .node-icon-4{font-size:40px;color:black;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-6p4OZQ9CYaPVe7I0 .edge-depth-4{stroke-width:2;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled,#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled circle,#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled text{fill:lightgray;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled text{fill:#efefef;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-5 rect,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-5 path,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-5 circle,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-5 polygon,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-5 text{fill:black;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .node-icon-5{font-size:40px;color:black;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-6p4OZQ9CYaPVe7I0 .edge-depth-5{stroke-width:-1;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled,#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled circle,#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled text{fill:lightgray;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled text{fill:#efefef;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-6 rect,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-6 path,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-6 circle,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-6 polygon,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-6 text{fill:black;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .node-icon-6{font-size:40px;color:black;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-6p4OZQ9CYaPVe7I0 .edge-depth-6{stroke-width:-4;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled,#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled circle,#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled text{fill:lightgray;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled text{fill:#efefef;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-7 rect,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-7 path,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-7 circle,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-7 polygon,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-7 text{fill:black;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .node-icon-7{font-size:40px;color:black;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-6p4OZQ9CYaPVe7I0 .edge-depth-7{stroke-width:-7;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled,#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled circle,#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled text{fill:lightgray;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled text{fill:#efefef;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-8 rect,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-8 path,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-8 circle,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-8 polygon,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-8 text{fill:black;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .node-icon-8{font-size:40px;color:black;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-6p4OZQ9CYaPVe7I0 .edge-depth-8{stroke-width:-10;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled,#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled circle,#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled text{fill:lightgray;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled text{fill:#efefef;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-9 rect,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-9 path,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-9 circle,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-9 polygon,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-9 text{fill:black;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .node-icon-9{font-size:40px;color:black;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-6p4OZQ9CYaPVe7I0 .edge-depth-9{stroke-width:-13;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled,#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled circle,#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled text{fill:lightgray;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled text{fill:#efefef;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-10 rect,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-10 path,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-10 circle,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-10 polygon,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-10 text{fill:black;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .node-icon-10{font-size:40px;color:black;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-6p4OZQ9CYaPVe7I0 .edge-depth-10{stroke-width:-16;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled,#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled circle,#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled text{fill:lightgray;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .disabled text{fill:#efefef;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-root rect,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-root path,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-root circle,#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-root text{fill:#ffffff;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-root span{color:#ffffff;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .section-2 span{color:#ffffff;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .edge{fill:none;}#mermaid-svg-6p4OZQ9CYaPVe7I0 .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-6p4OZQ9CYaPVe7I0 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Go Map 适用场景
键值缓存
用户ID→用户信息
Token→Session
索引查找
O 1 平均查找
去重计数
词频统计
配置映射
环境变量→值
特性开关→状态
数据去重
已处理ID集合
URL去重爬虫
2 底层 runtime 实现原理
⚠️ Go 1.26.4 的 map 实现已从传统的 hmap/bmap 迁移到 Swiss Table 设计(基于 Abseil),
核心代码在
internal/runtime/maps/包中。
2.1 Map 结构体完整字段说明
源码位置:internal/runtime/maps/map.go:191
go
type Map struct {
// ★ 已使用的插槽数量(即 map 中的元素个数)
// 必须放在第一个字段(编译器已知,用于 len() 内置函数)
used uint64
// ★ 哈希种子,每个 map 独有的随机数
// 防止哈希碰撞攻击
seed uintptr
// ★ 目录指针------指向 table 指针数组或单个 group
//
// 正常情况: dirPtr 指向数组 *[dirLen]*table
//
// 小 map 优化: 如果 map 始终只有 ≤8 个元素,
// dirPtr 直接指向单个 group(无需 table 开销)
// 此时 dirLen = 0
dirPtr unsafe.Pointer
// 目录长度 = 1 << globalDepth
dirLen int
// ★ 目录查找使用的比特数
// 哈希值的顶部 globalDepth 位用于选择 table
globalDepth uint8
// ★ 目录查找的位移量
// 64-bit 系统: globalShift = 64 - globalDepth
globalShift uint8
// ★ 写标志位
// 正常=0, 写操作时=1
// 如果多个 goroutine 并发写, XOR 1 使双方都能检测到竞态
writing uint8
// 是否可能存在墓碑(deleted 标记)
tombstonePossible bool
// Clear 调用的序列号,用于检测迭代期间的 clear
clearSeq uint64
}
内存布局图:
Map 结构体 (64-bit, 约 56 bytes)
┌────────────────────────────────────────────────────┐
│ used uint64 │ 元素数量(len()直接读)│
├────────────────────────────────────────────────────┤
│ seed uintptr │ 哈希种子(随机值) │
├────────────────────────────────────────────────────┤
│ dirPtr unsafe.P │ 目录/小map group指针 │
├────────────────────────────────────────────────────┤
│ dirLen int │ 目录长度 │
├────────────────────────────────────────────────────┤
│ globalDepth uint8 │ 目录查找比特数 │
│ globalShift uint8 │ 目录位移量 │
│ writing uint8 │ 写标志 │
│ tombstonePossible bool │ 墓碑标志 │
├────────────────────────────────────────────────────┤
│ clearSeq uint64 │ Clear 序列号 │
└────────────────────────────────────────────────────┘
2.2 table 结构体------单个 Swiss Table
源码位置:internal/runtime/maps/table.go:18
go
type table struct {
// 已填充的插槽数量
used uint16
// 总插槽数(2的幂次)= (groups.lengthMask+1) * MapGroupSlots
capacity uint16
// ★ 剩余可插入数(不含墓碑消耗的名额)
// 当 used + tombstones > loadFactor*capacity 时触发 rehash
growthLeft uint16
// 此 table 的目录深度(可能 < globalDepth)
localDepth uint8
// 此 table 在目录中的首个索引位置
// -1 表示已过期(被替换后不再在目录中)
index int
// ★ groups 数组------实际存储键值对的地方
groups groupsReference
}
table 结构体
┌─────────────────────────────────────────────┐
│ used uint16 │ 已用插槽数 │
│ capacity uint16 │ 总插槽数(2^N) │
│ growthLeft uint16 │ 剩余可插入数 │
│ localDepth uint8 │ 本地目录深度 │
│ index int │ 目录索引(-1=过期) │
│ groups groupsRef│ groups 数组引用 │
└─────────────────────────────────────────────┘
2.3 group 结构体------8 个插槽 + 控制字
源码位置:internal/runtime/maps/group.go:131
go
// 一个 group 的内存布局:
//
// type group struct {
// ctrls ctrlGroup // 8 字节控制字
// slots [MapGroupSlots]slot // 8 个 key/elem 插槽
// }
//
// type slot struct {
// key typ.Key
// elem typ.Elem
// }
type groupReference struct {
data unsafe.Pointer // 指向 group 的内存
}
ctrl(控制字节)编码:
每个 slot 有 1 字节控制信息:
empty: 1 0 0 0 0 0 0 0 (0x80) ← 空,未使用
deleted: 1 1 1 1 1 1 1 0 (0xFE) ← 已删除(墓碑)
full: 0 h h h h h h h ← 已使用,低7位 = H2(hash)
判断规则:
bit7=1 && bit1=0 → empty
bit7=1 && bit1=1 → deleted
bit7=0 → full (H2在低7位)
group 内存布局(以 map[string]int 为例)
┌──────────────────────────────────────────────────────┐
│ ctrlGroup (8 bytes) │
│ ├── ctrl[0]: 0x42 (full, H2=0x42) │
│ ├── ctrl[1]: 0x80 (empty) │
│ ├── ctrl[2]: 0xFE (deleted/墓碑) │
│ ├── ctrl[3]: 0x17 (full, H2=0x17) │
│ ├── ctrl[4]: 0x80 (empty) │
│ ├── ctrl[5]: 0x80 (empty) │
│ ├── ctrl[6]: 0x80 (empty) │
│ └── ctrl[7]: 0x80 (empty) │
├──────────────────────────────────────────────────────┤
│ slot[0]: key="alice", elem=90 │
│ slot[1]: (empty) │
│ slot[2]: (deleted) │
│ slot[3]: key="bob", elem=85 │
│ slot[4]: (empty) │
│ slot[5]: (empty) │
│ slot[6]: (empty) │
│ slot[7]: (empty) │
└──────────────────────────────────────────────────────┘
2.4 哈希函数、桶寻址、probe 探测序列
哈希分割:H1 与 H2
64-bit hash 值: hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhlllllll
│←──────────── H1 (57 bits) ────────────────────→│← H2(7b)→│
H1: 高 57 位 → 用于确定 probe 起始位置(哪个 group)
H2: 低 7 位 → 存储在控制字节中,用于快速过滤匹配
func h1(h uintptr) uintptr { return h >> 7 } // 取高57位
func h2(h uintptr) uintptr { return h & 0x7f } // 取低7位
#mermaid-svg-tve6UVNfkmuv0LIU{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-tve6UVNfkmuv0LIU .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tve6UVNfkmuv0LIU .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tve6UVNfkmuv0LIU .error-icon{fill:#552222;}#mermaid-svg-tve6UVNfkmuv0LIU .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tve6UVNfkmuv0LIU .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tve6UVNfkmuv0LIU .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tve6UVNfkmuv0LIU .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tve6UVNfkmuv0LIU .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tve6UVNfkmuv0LIU .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tve6UVNfkmuv0LIU .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tve6UVNfkmuv0LIU .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tve6UVNfkmuv0LIU .marker.cross{stroke:#333333;}#mermaid-svg-tve6UVNfkmuv0LIU svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tve6UVNfkmuv0LIU p{margin:0;}#mermaid-svg-tve6UVNfkmuv0LIU .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-tve6UVNfkmuv0LIU .cluster-label text{fill:#333;}#mermaid-svg-tve6UVNfkmuv0LIU .cluster-label span{color:#333;}#mermaid-svg-tve6UVNfkmuv0LIU .cluster-label span p{background-color:transparent;}#mermaid-svg-tve6UVNfkmuv0LIU .label text,#mermaid-svg-tve6UVNfkmuv0LIU span{fill:#333;color:#333;}#mermaid-svg-tve6UVNfkmuv0LIU .node rect,#mermaid-svg-tve6UVNfkmuv0LIU .node circle,#mermaid-svg-tve6UVNfkmuv0LIU .node ellipse,#mermaid-svg-tve6UVNfkmuv0LIU .node polygon,#mermaid-svg-tve6UVNfkmuv0LIU .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-tve6UVNfkmuv0LIU .rough-node .label text,#mermaid-svg-tve6UVNfkmuv0LIU .node .label text,#mermaid-svg-tve6UVNfkmuv0LIU .image-shape .label,#mermaid-svg-tve6UVNfkmuv0LIU .icon-shape .label{text-anchor:middle;}#mermaid-svg-tve6UVNfkmuv0LIU .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-tve6UVNfkmuv0LIU .rough-node .label,#mermaid-svg-tve6UVNfkmuv0LIU .node .label,#mermaid-svg-tve6UVNfkmuv0LIU .image-shape .label,#mermaid-svg-tve6UVNfkmuv0LIU .icon-shape .label{text-align:center;}#mermaid-svg-tve6UVNfkmuv0LIU .node.clickable{cursor:pointer;}#mermaid-svg-tve6UVNfkmuv0LIU .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-tve6UVNfkmuv0LIU .arrowheadPath{fill:#333333;}#mermaid-svg-tve6UVNfkmuv0LIU .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-tve6UVNfkmuv0LIU .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-tve6UVNfkmuv0LIU .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tve6UVNfkmuv0LIU .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-tve6UVNfkmuv0LIU .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tve6UVNfkmuv0LIU .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-tve6UVNfkmuv0LIU .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-tve6UVNfkmuv0LIU .cluster text{fill:#333;}#mermaid-svg-tve6UVNfkmuv0LIU .cluster span{color:#333;}#mermaid-svg-tve6UVNfkmuv0LIU div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-tve6UVNfkmuv0LIU .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-tve6UVNfkmuv0LIU rect.text{fill:none;stroke-width:0;}#mermaid-svg-tve6UVNfkmuv0LIU .icon-shape,#mermaid-svg-tve6UVNfkmuv0LIU .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tve6UVNfkmuv0LIU .icon-shape p,#mermaid-svg-tve6UVNfkmuv0LIU .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-tve6UVNfkmuv0LIU .icon-shape .label rect,#mermaid-svg-tve6UVNfkmuv0LIU .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tve6UVNfkmuv0LIU .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-tve6UVNfkmuv0LIU .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-tve6UVNfkmuv0LIU :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} key → hash(key)
64-bit hash
H1 = hash >> 7
高57位
H2 = hash & 0x7f
低7位
确定起始 group
probeSeq 起始偏移
与 ctrl byte 比较
8个slot并行匹配
probeSeq 探测序列
源码位置:internal/runtime/maps/table.go:530
go
// 二次探测序列: p(i) = hash + (i² + i)/2 mod (mask+1)
// 即: hash, hash+1, hash+3, hash+6, hash+10, ...
// 当 groups 数量是 2 的幂次时,此序列能遍历每个 group 恰好一次
type probeSeq struct {
mask uint64 // groups 数量 - 1
offset uint64 // 当前 group 偏移
index uint64 // 步进索引
}
func makeProbeSeq(hash uintptr, mask uint64) probeSeq {
return probeSeq{
mask: mask,
offset: uint64(hash) & mask, // H1 & mask = 起始 group
index: 0,
}
}
func (s probeSeq) next() probeSeq {
s.index++
s.offset = (s.offset + s.index) & s.mask // 二次探测步进
return s
}
#mermaid-svg-RFB9WPxifcGepie7{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-RFB9WPxifcGepie7 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-RFB9WPxifcGepie7 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-RFB9WPxifcGepie7 .error-icon{fill:#552222;}#mermaid-svg-RFB9WPxifcGepie7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-RFB9WPxifcGepie7 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-RFB9WPxifcGepie7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-RFB9WPxifcGepie7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-RFB9WPxifcGepie7 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-RFB9WPxifcGepie7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-RFB9WPxifcGepie7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-RFB9WPxifcGepie7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-RFB9WPxifcGepie7 .marker.cross{stroke:#333333;}#mermaid-svg-RFB9WPxifcGepie7 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-RFB9WPxifcGepie7 p{margin:0;}#mermaid-svg-RFB9WPxifcGepie7 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-RFB9WPxifcGepie7 .cluster-label text{fill:#333;}#mermaid-svg-RFB9WPxifcGepie7 .cluster-label span{color:#333;}#mermaid-svg-RFB9WPxifcGepie7 .cluster-label span p{background-color:transparent;}#mermaid-svg-RFB9WPxifcGepie7 .label text,#mermaid-svg-RFB9WPxifcGepie7 span{fill:#333;color:#333;}#mermaid-svg-RFB9WPxifcGepie7 .node rect,#mermaid-svg-RFB9WPxifcGepie7 .node circle,#mermaid-svg-RFB9WPxifcGepie7 .node ellipse,#mermaid-svg-RFB9WPxifcGepie7 .node polygon,#mermaid-svg-RFB9WPxifcGepie7 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-RFB9WPxifcGepie7 .rough-node .label text,#mermaid-svg-RFB9WPxifcGepie7 .node .label text,#mermaid-svg-RFB9WPxifcGepie7 .image-shape .label,#mermaid-svg-RFB9WPxifcGepie7 .icon-shape .label{text-anchor:middle;}#mermaid-svg-RFB9WPxifcGepie7 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-RFB9WPxifcGepie7 .rough-node .label,#mermaid-svg-RFB9WPxifcGepie7 .node .label,#mermaid-svg-RFB9WPxifcGepie7 .image-shape .label,#mermaid-svg-RFB9WPxifcGepie7 .icon-shape .label{text-align:center;}#mermaid-svg-RFB9WPxifcGepie7 .node.clickable{cursor:pointer;}#mermaid-svg-RFB9WPxifcGepie7 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-RFB9WPxifcGepie7 .arrowheadPath{fill:#333333;}#mermaid-svg-RFB9WPxifcGepie7 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-RFB9WPxifcGepie7 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-RFB9WPxifcGepie7 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RFB9WPxifcGepie7 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-RFB9WPxifcGepie7 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RFB9WPxifcGepie7 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-RFB9WPxifcGepie7 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-RFB9WPxifcGepie7 .cluster text{fill:#333;}#mermaid-svg-RFB9WPxifcGepie7 .cluster span{color:#333;}#mermaid-svg-RFB9WPxifcGepie7 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-RFB9WPxifcGepie7 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-RFB9WPxifcGepie7 rect.text{fill:none;stroke-width:0;}#mermaid-svg-RFB9WPxifcGepie7 .icon-shape,#mermaid-svg-RFB9WPxifcGepie7 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RFB9WPxifcGepie7 .icon-shape p,#mermaid-svg-RFB9WPxifcGepie7 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-RFB9WPxifcGepie7 .icon-shape .label rect,#mermaid-svg-RFB9WPxifcGepie7 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RFB9WPxifcGepie7 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-RFB9WPxifcGepie7 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-RFB9WPxifcGepie7 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 有匹配
Yes
No
无匹配
有空 slot
全满
查找 key='bob'
hash = Hasher('bob', seed)
H1 = hash >> 7
H2 = hash & 0x7f = 0x2A
seq = makeProbeSeq(H1, groupsMask)
检查 groupseq.offset
ctrl.matchH2(H2)
key == slot.key?
找到!返回 elem 指针
移除当前匹配,继续检查
ctrl.matchEmpty()
未找到(探测终止)
seq = seq.next()
探测下一个 group
2.5 哈希冲突解决------Swiss Table 方式
Go 1.26 采用 Swiss Table 设计(而非传统的拉链法),使用开放寻址 + 控制字节并行匹配:
传统拉链法 vs Swiss Table 对比:
传统拉链法(旧版 Go hmap/bmap):
bucket[0] → [k1][v1] → [k2][v2] → overflow bucket → ...
bucket[1] → [k3][v3] → ...
bucket[2] → ...
Swiss Table(Go 1.26+):
group[0]: ctrl=[0x42|0x80|0xFE|0x17|0x80|0x80|0x80|0x80]
slots=[k1,v1| |tomb|k3,v3| | | | ]
group[1]: ctrl=[0x2A|0x80|0x80|0x80|0x80|0x80|0x80|0x80]
slots=[k2,v2| | | | | | | ]
group[2]: ...
★ 冲突解决:同一 group 内通过 ctrl 并行比较 H2
不同 group 间通过 probeSeq 二次探测
★ 不再使用链表/溢出桶!
并行匹配的魔力 ------matchH2 一次检查 8 个 slot:
go
// matchH2: 用位运算同时比较 8 个控制字节的 H2
func ctrlGroupMatchH2(g ctrlGroup, h uintptr) bitset {
v := uint64(g) ^ (bitsetLSB * uint64(h)) // XOR 8 份 h
return bitset(((v - bitsetLSB) &^ v) & bitsetMSB) // 巧妙判断哪些字节匹配
}
// 这条指令在 AMD64 上会被编译为 SIMD 指令,一条指令检查 8 个字节!
2.6 扩容机制:grow 和 split
扩容触发条件
当 growthLeft == 0 时(即 used + tombstones 达到 loadFactor × capacity):
1. 先尝试 pruneTombstones(清除不需要的墓碑)
2. 如果墓碑不够多(< 10% capacity),则执行 rehash
两种扩容方式
go
func (t *table) rehash(typ *abi.MapType, m *Map) {
newCapacity := 2 * t.capacity
if newCapacity <= maxTableCapacity { // maxTableCapacity = 1024
t.grow(typ, m, newCapacity) // ← 增量扩容:2倍容量
return
}
t.split(typ, m) // ← 分裂:拆成两个 table
}
#mermaid-svg-GJqkbgopVJqhDIHb{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-GJqkbgopVJqhDIHb .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-GJqkbgopVJqhDIHb .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-GJqkbgopVJqhDIHb .error-icon{fill:#552222;}#mermaid-svg-GJqkbgopVJqhDIHb .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-GJqkbgopVJqhDIHb .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-GJqkbgopVJqhDIHb .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-GJqkbgopVJqhDIHb .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-GJqkbgopVJqhDIHb .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-GJqkbgopVJqhDIHb .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-GJqkbgopVJqhDIHb .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-GJqkbgopVJqhDIHb .marker{fill:#333333;stroke:#333333;}#mermaid-svg-GJqkbgopVJqhDIHb .marker.cross{stroke:#333333;}#mermaid-svg-GJqkbgopVJqhDIHb svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-GJqkbgopVJqhDIHb p{margin:0;}#mermaid-svg-GJqkbgopVJqhDIHb .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-GJqkbgopVJqhDIHb .cluster-label text{fill:#333;}#mermaid-svg-GJqkbgopVJqhDIHb .cluster-label span{color:#333;}#mermaid-svg-GJqkbgopVJqhDIHb .cluster-label span p{background-color:transparent;}#mermaid-svg-GJqkbgopVJqhDIHb .label text,#mermaid-svg-GJqkbgopVJqhDIHb span{fill:#333;color:#333;}#mermaid-svg-GJqkbgopVJqhDIHb .node rect,#mermaid-svg-GJqkbgopVJqhDIHb .node circle,#mermaid-svg-GJqkbgopVJqhDIHb .node ellipse,#mermaid-svg-GJqkbgopVJqhDIHb .node polygon,#mermaid-svg-GJqkbgopVJqhDIHb .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-GJqkbgopVJqhDIHb .rough-node .label text,#mermaid-svg-GJqkbgopVJqhDIHb .node .label text,#mermaid-svg-GJqkbgopVJqhDIHb .image-shape .label,#mermaid-svg-GJqkbgopVJqhDIHb .icon-shape .label{text-anchor:middle;}#mermaid-svg-GJqkbgopVJqhDIHb .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-GJqkbgopVJqhDIHb .rough-node .label,#mermaid-svg-GJqkbgopVJqhDIHb .node .label,#mermaid-svg-GJqkbgopVJqhDIHb .image-shape .label,#mermaid-svg-GJqkbgopVJqhDIHb .icon-shape .label{text-align:center;}#mermaid-svg-GJqkbgopVJqhDIHb .node.clickable{cursor:pointer;}#mermaid-svg-GJqkbgopVJqhDIHb .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-GJqkbgopVJqhDIHb .arrowheadPath{fill:#333333;}#mermaid-svg-GJqkbgopVJqhDIHb .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-GJqkbgopVJqhDIHb .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-GJqkbgopVJqhDIHb .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-GJqkbgopVJqhDIHb .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-GJqkbgopVJqhDIHb .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-GJqkbgopVJqhDIHb .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-GJqkbgopVJqhDIHb .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-GJqkbgopVJqhDIHb .cluster text{fill:#333;}#mermaid-svg-GJqkbgopVJqhDIHb .cluster span{color:#333;}#mermaid-svg-GJqkbgopVJqhDIHb div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-GJqkbgopVJqhDIHb .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-GJqkbgopVJqhDIHb rect.text{fill:none;stroke-width:0;}#mermaid-svg-GJqkbgopVJqhDIHb .icon-shape,#mermaid-svg-GJqkbgopVJqhDIHb .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-GJqkbgopVJqhDIHb .icon-shape p,#mermaid-svg-GJqkbgopVJqhDIHb .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-GJqkbgopVJqhDIHb .icon-shape .label rect,#mermaid-svg-GJqkbgopVJqhDIHb .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-GJqkbgopVJqhDIHb .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-GJqkbgopVJqhDIHb .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-GJqkbgopVJqhDIHb :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Yes
No
Yes
No
growthLeft == 0
pruneTombstones
清除后 growthLeft > 0?
继续插入 ✅
capacity * 2 ≤ 1024?
grow: 2倍扩容
单 table 容量翻倍
split: 分裂
拆成 2 个 table
目录可能翻倍
grow(增量扩容)
go
func (t *table) grow(typ *abi.MapType, m *Map, newCapacity uint16) {
newTable := newTable(typ, uint64(newCapacity), t.index, t.localDepth)
// 把旧 table 的所有有效 entry 重新插入新 table
// 新的 probeSeq 基于 2 倍的 groups 数量
for 每个有效 slot {
hash := typ.Hasher(key, m.seed)
newTable.uncheckedPutSlot(typ, hash, key, elem)
}
m.replaceTable(newTable) // 在目录中替换旧 table
t.index = -1 // 标记旧 table 为过期
}
split(分裂扩容------可扩展哈希)
go
func (t *table) split(typ *abi.MapType, m *Map) {
localDepth := t.localDepth + 1
left := newTable(typ, maxTableCapacity, -1, localDepth)
right := newTable(typ, maxTableCapacity, -1, localDepth)
mask := localDepthMask(localDepth) // 新增的1个比特位
for 每个有效 slot {
hash := typ.Hasher(key, m.seed)
if hash & mask == 0 {
left.uncheckedPutSlot(...) // 该比特=0 → left
} else {
right.uncheckedPutSlot(...) // 该比特=1 → right
}
}
m.installTableSplit(t, left, right) // 更新目录
}
目录扩展示例:
split 前(globalDepth=1, 2个目录项):
directory
+----+
| 0 | → table_A (localDepth=1)
+----+
| 1 | → table_B (localDepth=1) ← B 满了,要 split
+----+
split 后(globalDepth=2, 4个目录项):
directory
+----+
| 00 | → table_A (localDepth=1) ← A 没变,两个目录项指向它
+----+
| 01 | → table_A (localDepth=1)
+----+
| 10 | → table_B_left (localDepth=2) ← B 拆成两个
+----+
| 11 | → table_B_right (localDepth=2)
+----+
#mermaid-svg-nKj5oVCtSeQzLYHm{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-nKj5oVCtSeQzLYHm .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-nKj5oVCtSeQzLYHm .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-nKj5oVCtSeQzLYHm .error-icon{fill:#552222;}#mermaid-svg-nKj5oVCtSeQzLYHm .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-nKj5oVCtSeQzLYHm .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-nKj5oVCtSeQzLYHm .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-nKj5oVCtSeQzLYHm .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-nKj5oVCtSeQzLYHm .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-nKj5oVCtSeQzLYHm .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-nKj5oVCtSeQzLYHm .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-nKj5oVCtSeQzLYHm .marker{fill:#333333;stroke:#333333;}#mermaid-svg-nKj5oVCtSeQzLYHm .marker.cross{stroke:#333333;}#mermaid-svg-nKj5oVCtSeQzLYHm svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-nKj5oVCtSeQzLYHm p{margin:0;}#mermaid-svg-nKj5oVCtSeQzLYHm .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-nKj5oVCtSeQzLYHm .cluster-label text{fill:#333;}#mermaid-svg-nKj5oVCtSeQzLYHm .cluster-label span{color:#333;}#mermaid-svg-nKj5oVCtSeQzLYHm .cluster-label span p{background-color:transparent;}#mermaid-svg-nKj5oVCtSeQzLYHm .label text,#mermaid-svg-nKj5oVCtSeQzLYHm span{fill:#333;color:#333;}#mermaid-svg-nKj5oVCtSeQzLYHm .node rect,#mermaid-svg-nKj5oVCtSeQzLYHm .node circle,#mermaid-svg-nKj5oVCtSeQzLYHm .node ellipse,#mermaid-svg-nKj5oVCtSeQzLYHm .node polygon,#mermaid-svg-nKj5oVCtSeQzLYHm .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-nKj5oVCtSeQzLYHm .rough-node .label text,#mermaid-svg-nKj5oVCtSeQzLYHm .node .label text,#mermaid-svg-nKj5oVCtSeQzLYHm .image-shape .label,#mermaid-svg-nKj5oVCtSeQzLYHm .icon-shape .label{text-anchor:middle;}#mermaid-svg-nKj5oVCtSeQzLYHm .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-nKj5oVCtSeQzLYHm .rough-node .label,#mermaid-svg-nKj5oVCtSeQzLYHm .node .label,#mermaid-svg-nKj5oVCtSeQzLYHm .image-shape .label,#mermaid-svg-nKj5oVCtSeQzLYHm .icon-shape .label{text-align:center;}#mermaid-svg-nKj5oVCtSeQzLYHm .node.clickable{cursor:pointer;}#mermaid-svg-nKj5oVCtSeQzLYHm .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-nKj5oVCtSeQzLYHm .arrowheadPath{fill:#333333;}#mermaid-svg-nKj5oVCtSeQzLYHm .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-nKj5oVCtSeQzLYHm .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-nKj5oVCtSeQzLYHm .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-nKj5oVCtSeQzLYHm .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-nKj5oVCtSeQzLYHm .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-nKj5oVCtSeQzLYHm .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-nKj5oVCtSeQzLYHm .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-nKj5oVCtSeQzLYHm .cluster text{fill:#333;}#mermaid-svg-nKj5oVCtSeQzLYHm .cluster span{color:#333;}#mermaid-svg-nKj5oVCtSeQzLYHm div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-nKj5oVCtSeQzLYHm .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-nKj5oVCtSeQzLYHm rect.text{fill:none;stroke-width:0;}#mermaid-svg-nKj5oVCtSeQzLYHm .icon-shape,#mermaid-svg-nKj5oVCtSeQzLYHm .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-nKj5oVCtSeQzLYHm .icon-shape p,#mermaid-svg-nKj5oVCtSeQzLYHm .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-nKj5oVCtSeQzLYHm .icon-shape .label rect,#mermaid-svg-nKj5oVCtSeQzLYHm .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-nKj5oVCtSeQzLYHm .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-nKj5oVCtSeQzLYHm .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-nKj5oVCtSeQzLYHm :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Split 后
Split 前
B 满了, split
directory
0→A
1→B
directory
00→A
01→A
10→B_left
11→B_right
2.7 删除机制与墓碑(tombstone)
go
func (t *table) Delete(typ *abi.MapType, m *Map, hash uintptr, key unsafe.Pointer) bool {
// 1. 通过 probeSeq 找到 key
// 2. 清除 key 和 elem
// 3. 设置控制字节:
if g.ctrls().matchEmpty() != 0 {
// ★ group 内有空 slot → 可以安全设为 empty
// 因为探测不会跳过有空 slot 的 group
g.ctrls().set(i, ctrlEmpty)
t.growthLeft++ // 恢复一个 growthLeft
} else {
// ★ group 全满 → 必须设为 deleted(墓碑)
// 如果设为 empty,正在探测的查找会提前终止!
g.ctrls().set(i, ctrlDeleted) // 0xFE
// growthLeft 不增加(墓碑仍占名额)
}
}
墓碑机制的核心逻辑:
插入 k1 → hash(k1)%4 = 0 → group[0] 满 → 探测到 group[1] 插入
查找 k1: group[0] → 不匹配 → group[1] → 找到 ✅
如果删除 k1 时 group[0] 变为 empty 而非 deleted:
查找 k1': hash(k1')%4 = 0 → group[0] 有 empty → 探测终止 → 未找到 ❌
但 k1' 实际在 group[1]!
所以:group 全满时删除必须用墓碑,不能设为 empty!
#mermaid-svg-1SRDSJPjVcEuop9N{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-1SRDSJPjVcEuop9N .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-1SRDSJPjVcEuop9N .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-1SRDSJPjVcEuop9N .error-icon{fill:#552222;}#mermaid-svg-1SRDSJPjVcEuop9N .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-1SRDSJPjVcEuop9N .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-1SRDSJPjVcEuop9N .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-1SRDSJPjVcEuop9N .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-1SRDSJPjVcEuop9N .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-1SRDSJPjVcEuop9N .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-1SRDSJPjVcEuop9N .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-1SRDSJPjVcEuop9N .marker{fill:#333333;stroke:#333333;}#mermaid-svg-1SRDSJPjVcEuop9N .marker.cross{stroke:#333333;}#mermaid-svg-1SRDSJPjVcEuop9N svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-1SRDSJPjVcEuop9N p{margin:0;}#mermaid-svg-1SRDSJPjVcEuop9N .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-1SRDSJPjVcEuop9N .cluster-label text{fill:#333;}#mermaid-svg-1SRDSJPjVcEuop9N .cluster-label span{color:#333;}#mermaid-svg-1SRDSJPjVcEuop9N .cluster-label span p{background-color:transparent;}#mermaid-svg-1SRDSJPjVcEuop9N .label text,#mermaid-svg-1SRDSJPjVcEuop9N span{fill:#333;color:#333;}#mermaid-svg-1SRDSJPjVcEuop9N .node rect,#mermaid-svg-1SRDSJPjVcEuop9N .node circle,#mermaid-svg-1SRDSJPjVcEuop9N .node ellipse,#mermaid-svg-1SRDSJPjVcEuop9N .node polygon,#mermaid-svg-1SRDSJPjVcEuop9N .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-1SRDSJPjVcEuop9N .rough-node .label text,#mermaid-svg-1SRDSJPjVcEuop9N .node .label text,#mermaid-svg-1SRDSJPjVcEuop9N .image-shape .label,#mermaid-svg-1SRDSJPjVcEuop9N .icon-shape .label{text-anchor:middle;}#mermaid-svg-1SRDSJPjVcEuop9N .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-1SRDSJPjVcEuop9N .rough-node .label,#mermaid-svg-1SRDSJPjVcEuop9N .node .label,#mermaid-svg-1SRDSJPjVcEuop9N .image-shape .label,#mermaid-svg-1SRDSJPjVcEuop9N .icon-shape .label{text-align:center;}#mermaid-svg-1SRDSJPjVcEuop9N .node.clickable{cursor:pointer;}#mermaid-svg-1SRDSJPjVcEuop9N .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-1SRDSJPjVcEuop9N .arrowheadPath{fill:#333333;}#mermaid-svg-1SRDSJPjVcEuop9N .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-1SRDSJPjVcEuop9N .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-1SRDSJPjVcEuop9N .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1SRDSJPjVcEuop9N .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-1SRDSJPjVcEuop9N .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1SRDSJPjVcEuop9N .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-1SRDSJPjVcEuop9N .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-1SRDSJPjVcEuop9N .cluster text{fill:#333;}#mermaid-svg-1SRDSJPjVcEuop9N .cluster span{color:#333;}#mermaid-svg-1SRDSJPjVcEuop9N div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-1SRDSJPjVcEuop9N .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-1SRDSJPjVcEuop9N rect.text{fill:none;stroke-width:0;}#mermaid-svg-1SRDSJPjVcEuop9N .icon-shape,#mermaid-svg-1SRDSJPjVcEuop9N .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1SRDSJPjVcEuop9N .icon-shape p,#mermaid-svg-1SRDSJPjVcEuop9N .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-1SRDSJPjVcEuop9N .icon-shape .label rect,#mermaid-svg-1SRDSJPjVcEuop9N .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1SRDSJPjVcEuop9N .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-1SRDSJPjVcEuop9N .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-1SRDSJPjVcEuop9N :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Yes
No
删除 slot
group 内有空 slot?
ctrl = empty (0x80)
growthLeft++
✅ 简单删除
ctrl = deleted (0xFE)
growthLeft 不变
⚠️ 墓碑标记
2.8 map 内存初始化:var 声明 vs make
var 声明------零值 nil
go
var m map[string]int
// m = nil(Map 指针为 nil)
// m == nil → true
// len(m) → 0
// m["key"] → 0(读取不 panic,返回零值)
// m["key"] = 1 → panic! assignment to entry in nil map
// delete(m, "key") → 不 panic(no-op)
// for range m → 不执行循环体
底层原因:runtime 中 nil map 的检测:
go
// runtime/map.go
var maps_errNilAssign error = plainError("assignment to entry in nil map")
// 赋值操作编译为 mapassign 调用
// 内部会检查 m == nil → panic(maps_errNilAssign)
make 初始化------分配 Map 结构体
go
m := make(map[string]int) // 调用 makemap_small() → 不预分配 group
m := make(map[string]int, 100) // 调用 makemap() → 按需分配 groups
// makemap_small: 小 map 优化
func makemap_small() *maps.Map {
return maps.NewEmptyMap() // 只分配 Map 结构体,不分配 group
// 首次赋值时再分配
}
// makemap: 带 hint 的初始化
func makemap(t *abi.MapType, hint int, m *maps.Map) *maps.Map {
if hint <= abi.MapGroupSlots { // ≤8 个元素
return m // 小 map,不需要预分配
}
// 计算 targetCapacity = (hint * 8) / 7
// 分配 groups 和 directory
return maps.NewMap(t, uintptr(hint), m, maxAlloc)
}
#mermaid-svg-pkKSms0Jq8WYUUsu{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-pkKSms0Jq8WYUUsu .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-pkKSms0Jq8WYUUsu .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-pkKSms0Jq8WYUUsu .error-icon{fill:#552222;}#mermaid-svg-pkKSms0Jq8WYUUsu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-pkKSms0Jq8WYUUsu .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-pkKSms0Jq8WYUUsu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-pkKSms0Jq8WYUUsu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-pkKSms0Jq8WYUUsu .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-pkKSms0Jq8WYUUsu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-pkKSms0Jq8WYUUsu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-pkKSms0Jq8WYUUsu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-pkKSms0Jq8WYUUsu .marker.cross{stroke:#333333;}#mermaid-svg-pkKSms0Jq8WYUUsu svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-pkKSms0Jq8WYUUsu p{margin:0;}#mermaid-svg-pkKSms0Jq8WYUUsu .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-pkKSms0Jq8WYUUsu .cluster-label text{fill:#333;}#mermaid-svg-pkKSms0Jq8WYUUsu .cluster-label span{color:#333;}#mermaid-svg-pkKSms0Jq8WYUUsu .cluster-label span p{background-color:transparent;}#mermaid-svg-pkKSms0Jq8WYUUsu .label text,#mermaid-svg-pkKSms0Jq8WYUUsu span{fill:#333;color:#333;}#mermaid-svg-pkKSms0Jq8WYUUsu .node rect,#mermaid-svg-pkKSms0Jq8WYUUsu .node circle,#mermaid-svg-pkKSms0Jq8WYUUsu .node ellipse,#mermaid-svg-pkKSms0Jq8WYUUsu .node polygon,#mermaid-svg-pkKSms0Jq8WYUUsu .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-pkKSms0Jq8WYUUsu .rough-node .label text,#mermaid-svg-pkKSms0Jq8WYUUsu .node .label text,#mermaid-svg-pkKSms0Jq8WYUUsu .image-shape .label,#mermaid-svg-pkKSms0Jq8WYUUsu .icon-shape .label{text-anchor:middle;}#mermaid-svg-pkKSms0Jq8WYUUsu .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-pkKSms0Jq8WYUUsu .rough-node .label,#mermaid-svg-pkKSms0Jq8WYUUsu .node .label,#mermaid-svg-pkKSms0Jq8WYUUsu .image-shape .label,#mermaid-svg-pkKSms0Jq8WYUUsu .icon-shape .label{text-align:center;}#mermaid-svg-pkKSms0Jq8WYUUsu .node.clickable{cursor:pointer;}#mermaid-svg-pkKSms0Jq8WYUUsu .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-pkKSms0Jq8WYUUsu .arrowheadPath{fill:#333333;}#mermaid-svg-pkKSms0Jq8WYUUsu .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-pkKSms0Jq8WYUUsu .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-pkKSms0Jq8WYUUsu .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pkKSms0Jq8WYUUsu .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-pkKSms0Jq8WYUUsu .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pkKSms0Jq8WYUUsu .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-pkKSms0Jq8WYUUsu .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-pkKSms0Jq8WYUUsu .cluster text{fill:#333;}#mermaid-svg-pkKSms0Jq8WYUUsu .cluster span{color:#333;}#mermaid-svg-pkKSms0Jq8WYUUsu div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-pkKSms0Jq8WYUUsu .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-pkKSms0Jq8WYUUsu rect.text{fill:none;stroke-width:0;}#mermaid-svg-pkKSms0Jq8WYUUsu .icon-shape,#mermaid-svg-pkKSms0Jq8WYUUsu .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pkKSms0Jq8WYUUsu .icon-shape p,#mermaid-svg-pkKSms0Jq8WYUUsu .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-pkKSms0Jq8WYUUsu .icon-shape .label rect,#mermaid-svg-pkKSms0Jq8WYUUsu .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pkKSms0Jq8WYUUsu .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-pkKSms0Jq8WYUUsu .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-pkKSms0Jq8WYUUsu :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Yes
No
make(mapKV, hint)
hint ≤ 8?
NewEmptyMap()
只分配 Map 结构体
不分配 group
首次赋值时再分配
NewMap(hint)
计算 targetCapacity
分配 groups + directory
2.9 遍历 map 随机顺序的底层原因
源码位置:internal/runtime/maps/table.go Iter.Init
go
func (it *Iter) Init(typ *abi.MapType, m *maps.Map) {
// ...
it.entryOffset = rand() // ★ 随机 slot 起始偏移!
it.dirOffset = rand() // ★ 随机目录起始偏移!
// ...
}
两层随机化:
- entryOffset:在 group 内,从随机 slot 开始遍历
- dirOffset:在目录中,从随机 table 开始遍历
为什么这样做?
Go 语言规范明确要求迭代顺序是未指定的(unspecified),Go 团队刻意随机化遍历顺序,防止程序员依赖特定的迭代顺序------因为哈希表的实现可能在不同版本中改变,依赖顺序的代码不可移植。
3 示例代码逐行解析
3.1 声明与初始化
go
// ── 声明(零值 nil map)──
var m1 map[string]int
// [编译器] m1 = nil(*Map 指针为 nil)
// [注意] 可以读取 m1["key"] → 返回 0
// [注意] 不能写入 m1["key"] = 1 → panic!
// ── make 初始化 ──
m2 := make(map[string]int)
// [编译器] 调用 makemap_small()
// [运行时] 分配 Map 结构体 {used:0, seed:random, dirPtr:nil, dirLen:0}
// 首次赋值时分配第一个 group
// ── make 带 hint ──
m3 := make(map[string]int, 100)
// [编译器] 调用 makemap(mapType, 100, nil)
// [运行时] 计算需要 (100*8)/7 ≈ 115 个 slot
// 分配 Map + directory + groups
// 目录有多个 table,每个 table 有 8-1024 个 slot
// ── 字面量 ──
m4 := map[string]int{"a": 1, "b": 2}
// [编译器] 生成初始化代码
// 先 makemap_small(),再逐个 mapassign
3.2 赋值(写入)
go
m["alice"] = 90
// [编译器] 生成调用 mapassign(mapType, m, &"alice")
// [运行时] 底层流程:
// 1. 检查 m.writing != 0 → 如果正在写,fatal("concurrent map write")
// 2. 设置 m.writing = 1
// 3. hash = Hasher("alice", m.seed) // 计算哈希
// 4. H1 = hash >> 7, H2 = hash & 0x7f
// 5. directoryIndex = hash >> m.globalShift // 选择 table
// 6. 在 table 中 PutSlot:
// a. seq = makeProbeSeq(H1, groupsMask)
// b. group[seq.offset].ctrl.matchH2(H2) // 并行匹配
// c. 如果匹配到且 key 相等 → 更新 elem → 返回
// d. 如果有空/墓碑 slot → 插入新 entry
// e. 如果 growthLeft == 0 → pruneTombstones → rehash → 重试
// 7. 设置 m.writing = 0
3.3 取值(读取)
go
v := m["alice"]
// [编译器] 生成调用 mapaccess1(mapType, m, &"alice")
// [运行时] 底层流程:
// 1. 如果 m == nil → 返回零值指针(&zeroVal)
// 2. hash = Hasher("alice", m.seed)
// 3. 选择 table,在 table.Get 中:
// a. seq = makeProbeSeq(H1, groupsMask)
// b. group[seq.offset].ctrl.matchH2(H2)
// c. 匹配到且 key 相等 → 返回 elem 指针
// d. 匹配到 empty slot → 返回 zeroVal 指针
// e. 全满 → seq.next() 继续探测
v, ok := m["alice"]
// [编译器] 生成调用 mapaccess2(mapType, m, &"alice")
// 返回 (elem指针, bool)
// ok=true 表示 key 存在,ok=false 表示不存在
3.4 删除 delete
go
delete(m, "alice")
// [编译器] 生成调用 mapdelete(mapType, m, &"alice")
// [运行时] 底层流程:
// 1. 检查并发写
// 2. hash = Hasher("alice", m.seed)
// 3. 在 table.Delete 中:
// a. probeSeq 找到 key
// b. 清除 key 和 elem(typedmemclr)
// c. 如果 group 有空 slot → ctrl = empty, growthLeft++
// d. 如果 group 全满 → ctrl = deleted(墓碑)
// 4. m.used--
// 注意: delete 不存在的 key 是 no-op,不会 panic
3.5 长度 len
go
n := len(m)
// [编译器] 直接读取 m.used 字段(第一个字段,偏移=0)
// [运行时] 无函数调用,直接从 Map 结构体读取
// 即使 m == nil 也能工作(nil map 的 len 返回 0)
3.6 遍历 for range
go
for k, v := range m {
fmt.Println(k, v)
}
// [编译器] 生成:
// var it maps.Iter
// mapIterStart(mapType, m, &it) // 初始化 + 第一次 Next
// for it.Key() != nil {
// k = *it.Key()
// v = *it.Elem()
// // ... 循环体 ...
// mapIterNext(&it)
// }
// [运行时] Iter.Init 设置随机 entryOffset 和 dirOffset
// Next() 从随机位置开始遍历所有 group 的所有 slot
// 跳过 empty 和 deleted 的 slot
3.7 nil map 直接赋值 panic 触发
go
var m map[string]int
m["key"] = 1 // panic: assignment to entry in nil map
// [运行时] mapassign 内部:
// if m == nil {
// panic(plainError("assignment to entry in nil map"))
// }
// 触发位置:runtime/map.go 的 maps_errNilAssign 变量
3.8 key 不存在返回零值细节
go
v := m["nonexistent"] // 返回 0(int 的零值)
// [运行时] mapaccess1 返回 unsafe.Pointer(&zeroVal[0])
// 编译器对零值有特殊处理:
// - 如果 value 类型大小 > 0,mapaccess1_fat 传入 zero 指针
// - 找不到 key 时返回 zero 指针(指向预分配的零值)
// - 而不是返回 nil(因为 elem 指针不能为 nil)
4 完整使用示例合集
4.1 基础增删改查
go
package main
import "fmt"
func main() {
// 创建
m := make(map[string]int)
// 增
m["alice"] = 90
m["bob"] = 85
m["charlie"] = 78
fmt.Println("after add:", m) // map[alice:90 bob:85 charlie:78]
// 查(两种方式)
v1 := m["alice"] // 不存在返回零值 0
v2, ok := m["david"] // ok=false 表示不存在
fmt.Println(v1, v2, ok) // 90 0 false
// 改
m["alice"] = 95
fmt.Println("after update:", m["alice"]) // 95
// 删
delete(m, "bob")
fmt.Println("after delete:", m) // map[alice:95 charlie:78]
// 长度
fmt.Println("len:", len(m)) // 2
}
4.2 结构体/切片作为 value
go
type Student struct {
Name string
Score int
}
func main() {
// 结构体作为 value
m1 := map[int]Student{
1: {Name: "Alice", Score: 90},
2: {Name: "Bob", Score: 85},
}
m1[1].Score = 95 // ✅ Go 1.20+ 支持直接修改
fmt.Println(m1[1])
// 切片作为 value
m2 := map[string][]string{
"fruits": {"apple", "banana"},
"veggies": {"carrot", "celery"},
}
m2["fruits"] = append(m2["fruits"], "cherry") // ✅ append 返回新切片
fmt.Println(m2["fruits"])
}
4.3 map 嵌套 map
go
func main() {
// 二维 map
m := map[string]map[string]int{}
// 必须初始化内层 map!
m["class1"] = map[string]int{"alice": 90, "bob": 85}
m["class2"] = map[string]int{"charlie": 78}
// 查找
if class, ok := m["class1"]; ok {
if score, ok := class["alice"]; ok {
fmt.Println("alice score:", score) // 90
}
}
// 安全写入辅助函数
ensureMap(m, "class3")["david"] = 88
fmt.Println(m)
}
func ensureMap(m map[string]map[string]int, key string) map[string]int {
if m[key] == nil {
m[key] = make(map[string]int)
}
return m[key]
}
4.4 map 有序遍历
go
import "sort"
func main() {
m := map[string]int{
"charlie": 78,
"alice": 90,
"bob": 85,
}
// 方法1:收集 key 排序
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
fmt.Printf("%s: %d\n", k, m[k])
}
// alice: 90
// bob: 85
// charlie: 78
// 方法2:按 value 排序
type entry struct{ Key string; Value int }
entries := make([]entry, 0, len(m))
for k, v := range m {
entries = append(entries, entry{k, v})
}
sort.Slice(entries, func(i, j int) bool {
return entries[i].Value > entries[j].Value // 降序
})
for _, e := range entries {
fmt.Printf("%s: %d\n", e.Key, e.Value)
}
}
5 并发安全专题详解
5.1 原生 map 非并发安全底层原因
go
// Map 结构体中有 writing 标志位
type Map struct {
writing uint8 // 写操作时设为 1
// ...
}
// 写操作开始时:
m.writing = 1 // 或 XOR 1(并发写时双方都能检测到)
// 读操作时检查:
if m.writing != 0 {
fatal("concurrent map iteration and map write")
}
// 写操作结束时:
m.writing = 0 // 或 XOR 1
为什么不用锁而是 fatal?
- 性能:每次读写都加锁太重,标准库 map 是高性能设计的
- 设计哲学:map 的竞态是程序 bug,应该暴露而非静默忍受
- 不可恢复:竞态可能导致数据结构损坏,继续执行结果未定义
Goroutine 2 Map Goroutine 1 Goroutine 2 Map Goroutine 1 #mermaid-svg-TyWF8dGXBCDltMhq{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-TyWF8dGXBCDltMhq .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TyWF8dGXBCDltMhq .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TyWF8dGXBCDltMhq .error-icon{fill:#552222;}#mermaid-svg-TyWF8dGXBCDltMhq .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TyWF8dGXBCDltMhq .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TyWF8dGXBCDltMhq .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TyWF8dGXBCDltMhq .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TyWF8dGXBCDltMhq .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TyWF8dGXBCDltMhq .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TyWF8dGXBCDltMhq .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TyWF8dGXBCDltMhq .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TyWF8dGXBCDltMhq .marker.cross{stroke:#333333;}#mermaid-svg-TyWF8dGXBCDltMhq svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TyWF8dGXBCDltMhq p{margin:0;}#mermaid-svg-TyWF8dGXBCDltMhq .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-TyWF8dGXBCDltMhq text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-TyWF8dGXBCDltMhq .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-TyWF8dGXBCDltMhq .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-TyWF8dGXBCDltMhq .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-TyWF8dGXBCDltMhq .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-TyWF8dGXBCDltMhq #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-TyWF8dGXBCDltMhq .sequenceNumber{fill:white;}#mermaid-svg-TyWF8dGXBCDltMhq #sequencenumber{fill:#333;}#mermaid-svg-TyWF8dGXBCDltMhq #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-TyWF8dGXBCDltMhq .messageText{fill:#333;stroke:none;}#mermaid-svg-TyWF8dGXBCDltMhq .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-TyWF8dGXBCDltMhq .labelText,#mermaid-svg-TyWF8dGXBCDltMhq .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-TyWF8dGXBCDltMhq .loopText,#mermaid-svg-TyWF8dGXBCDltMhq .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-TyWF8dGXBCDltMhq .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-TyWF8dGXBCDltMhq .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-TyWF8dGXBCDltMhq .noteText,#mermaid-svg-TyWF8dGXBCDltMhq .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-TyWF8dGXBCDltMhq .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-TyWF8dGXBCDltMhq .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-TyWF8dGXBCDltMhq .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-TyWF8dGXBCDltMhq .actorPopupMenu{position:absolute;}#mermaid-svg-TyWF8dGXBCDltMhq .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-TyWF8dGXBCDltMhq .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-TyWF8dGXBCDltMhq .actor-man circle,#mermaid-svg-TyWF8dGXBCDltMhq line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-TyWF8dGXBCDltMhq :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 程序崩溃 💥 m"k" = v (设置 writing=1)m"k2" = v2 (检测 writing!=0)fatal("concurrent map writes")
5.2 sync.RWMutex 封装并发安全 map
go
package safemap
import "sync"
type SafeMap[K comparable, V any] struct {
mu sync.RWMutex
m map[K]V
}
func New[K comparable, V any]() *SafeMap[K, V] {
return &SafeMap[K, V]{
m: make(map[K]V),
}
}
// 读操作:RLock 允许多个读者并发
func (s *SafeMap[K, V]) Get(key K) (V, bool) {
s.mu.RLock()
defer s.mu.RUnlock()
v, ok := s.m[key]
return v, ok
}
// 写操作:Lock 独占
func (s *SafeMap[K, V]) Set(key K, value V) {
s.mu.Lock()
defer s.mu.Unlock()
s.m[key] = value
}
// 删除:Lock 独占
func (s *SafeMap[K, V]) Delete(key K) {
s.mu.Lock()
defer s.mu.Unlock()
delete(s.m, key)
}
// 遍历:RLock
func (s *SafeMap[K, V]) Range(fn func(K, V) bool) {
s.mu.RLock()
defer s.mu.RUnlock()
for k, v := range s.m {
if !fn(k, v) {
break
}
}
}
// Len:RLock
func (s *SafeMap[K, V]) Len() int {
s.mu.RLock()
defer s.mu.RUnlock()
return len(s.m)
}
读写锁性能特征:
操作 锁类型 并发性
Get RLock ✅ 多读者并发
Set Lock ❌ 独占
Delete Lock ❌ 独占
Range RLock ✅ 多读者并发
适用场景:读多写少(如配置缓存、白名单)
不适用场景:写频繁(Lock 竞争严重)
5.3 sync.Map 适用场景对比
Go 1.9+ 引入的 sync.Map 是无锁读的并发安全 map:
go
var m sync.Map
m.Store("key", "value") // 写
v, ok := m.Load("key") // 读
v, ok := m.LoadOrStore("k", "v") // 读或写
m.Delete("key") // 删
m.Range(func(k, v any) bool { // 遍历
fmt.Println(k, v)
return true
})
sync.Map 内部设计:
sync.Map 结构:
read atomic.Value → readOnly{amended: bool, m: map[any]*entry}
dirty map[any]*entry
entry 状态:
- *expunged → 已删除且不在 dirty 中
- nil → 已删除但在 dirty 中
- 正常指针 → 有效值
读路径:
1. 先查 read(原子读,无锁)→ 命中 → 直接返回 ⚡
2. 未命中 + amended=true → 加锁查 dirty → 找到 → 返回
3. 未命中 → 返回 false
写路径:
1. 先查 read → key 存在 → CAS 更新 entry(无锁)⚡
2. key 不存在或 expunged → 加锁 → 写 dirty → 可能提升 dirty 到 read
三种方案对比:
| 维度 | map + RWMutex | sync.Map | 原生 map |
|---|---|---|---|
| 读性能 | 中等(RLock) | 快(原子读) | 最快(无锁) |
| 写性能 | 中等(Lock) | 慢(dirty 提升) | 最快(但并发崩溃) |
| 适用场景 | 读多写少 | 读远多于写,key 稳定 | 单 goroutine |
| 类型安全 | 泛型可用 | any 接口 |
泛型可用 |
| 遍历安全 | RLock 保护 | 快照语义 | 不并发安全 |
#mermaid-svg-N4sJXPmnMcTTU3zk{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-N4sJXPmnMcTTU3zk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-N4sJXPmnMcTTU3zk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-N4sJXPmnMcTTU3zk .error-icon{fill:#552222;}#mermaid-svg-N4sJXPmnMcTTU3zk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-N4sJXPmnMcTTU3zk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-N4sJXPmnMcTTU3zk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-N4sJXPmnMcTTU3zk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-N4sJXPmnMcTTU3zk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-N4sJXPmnMcTTU3zk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-N4sJXPmnMcTTU3zk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-N4sJXPmnMcTTU3zk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-N4sJXPmnMcTTU3zk .marker.cross{stroke:#333333;}#mermaid-svg-N4sJXPmnMcTTU3zk svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-N4sJXPmnMcTTU3zk p{margin:0;}#mermaid-svg-N4sJXPmnMcTTU3zk .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-N4sJXPmnMcTTU3zk .cluster-label text{fill:#333;}#mermaid-svg-N4sJXPmnMcTTU3zk .cluster-label span{color:#333;}#mermaid-svg-N4sJXPmnMcTTU3zk .cluster-label span p{background-color:transparent;}#mermaid-svg-N4sJXPmnMcTTU3zk .label text,#mermaid-svg-N4sJXPmnMcTTU3zk span{fill:#333;color:#333;}#mermaid-svg-N4sJXPmnMcTTU3zk .node rect,#mermaid-svg-N4sJXPmnMcTTU3zk .node circle,#mermaid-svg-N4sJXPmnMcTTU3zk .node ellipse,#mermaid-svg-N4sJXPmnMcTTU3zk .node polygon,#mermaid-svg-N4sJXPmnMcTTU3zk .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-N4sJXPmnMcTTU3zk .rough-node .label text,#mermaid-svg-N4sJXPmnMcTTU3zk .node .label text,#mermaid-svg-N4sJXPmnMcTTU3zk .image-shape .label,#mermaid-svg-N4sJXPmnMcTTU3zk .icon-shape .label{text-anchor:middle;}#mermaid-svg-N4sJXPmnMcTTU3zk .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-N4sJXPmnMcTTU3zk .rough-node .label,#mermaid-svg-N4sJXPmnMcTTU3zk .node .label,#mermaid-svg-N4sJXPmnMcTTU3zk .image-shape .label,#mermaid-svg-N4sJXPmnMcTTU3zk .icon-shape .label{text-align:center;}#mermaid-svg-N4sJXPmnMcTTU3zk .node.clickable{cursor:pointer;}#mermaid-svg-N4sJXPmnMcTTU3zk .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-N4sJXPmnMcTTU3zk .arrowheadPath{fill:#333333;}#mermaid-svg-N4sJXPmnMcTTU3zk .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-N4sJXPmnMcTTU3zk .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-N4sJXPmnMcTTU3zk .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-N4sJXPmnMcTTU3zk .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-N4sJXPmnMcTTU3zk .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-N4sJXPmnMcTTU3zk .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-N4sJXPmnMcTTU3zk .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-N4sJXPmnMcTTU3zk .cluster text{fill:#333;}#mermaid-svg-N4sJXPmnMcTTU3zk .cluster span{color:#333;}#mermaid-svg-N4sJXPmnMcTTU3zk div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-N4sJXPmnMcTTU3zk .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-N4sJXPmnMcTTU3zk rect.text{fill:none;stroke-width:0;}#mermaid-svg-N4sJXPmnMcTTU3zk .icon-shape,#mermaid-svg-N4sJXPmnMcTTU3zk .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-N4sJXPmnMcTTU3zk .icon-shape p,#mermaid-svg-N4sJXPmnMcTTU3zk .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-N4sJXPmnMcTTU3zk .icon-shape .label rect,#mermaid-svg-N4sJXPmnMcTTU3zk .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-N4sJXPmnMcTTU3zk .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-N4sJXPmnMcTTU3zk .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-N4sJXPmnMcTTU3zk :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} No
Yes
读远多于写
key 稳定
读写均衡
或 key 频繁变化
并发访问 map?
原生 map
最快最简单
读多写少?
sync.Map
无锁读 ⚡
map + RWMutex
通用方案
6 经典问题与优化
6.1 遍历中删除/新增元素的风险
Go 的行为:
迭代语义(Go 规范):
1. ✅ 迭代期间删除的元素------不会在后续迭代中出现
2. ✅ 迭代期间新增的元素------可能出现也可能不出现
3. ✅ 迭代期间修改的元素------返回最新值
4. ✅ 不会返回同一个 entry 两次
安全删除方式:
go
// ❌ 危险:在其他 goroutine 删除(并发问题)
// ✅ 安全:在同一个 goroutine 中用 for range 删除
// 方式1:for range 直接 delete(安全)
for k, v := range m {
if v < 0 {
delete(m, k) // ✅ 安全,Go 保证不会重复或遗漏
}
}
// 方式2:收集 key 后再删除(更安全,逻辑更清晰)
var toDelete []string
for k, v := range m {
if v < 0 {
toDelete = append(toDelete, k)
}
}
for _, k := range toDelete {
delete(m, k)
}
6.2 map key 类型限制原因
为什么切片、map、函数不能做 key?
根本原因:这些类型不可比较(not comparable) ,而 map 的查找依赖于 key 的 == 比较。
go
// 切片不可比较的原因:
a := []int{1, 2, 3}
b := []int{1, 2, 3}
// a == b → 编译错误!切片不可比较
// 原因:切片是引用类型,如果 == 比较的是内容,那每次比较都要遍历
// 如果 == 比较的是指针,那语义不直观(相同内容但不同地址就不等)
// 解决方案:转换为可比较类型
m[string(a)] = value // 切片转字符串
m[fmt.Sprintf("%v", a)] // 格式化为字符串 key
#mermaid-svg-GyryOfOZ9kEFF8AW{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-GyryOfOZ9kEFF8AW .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-GyryOfOZ9kEFF8AW .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-GyryOfOZ9kEFF8AW .error-icon{fill:#552222;}#mermaid-svg-GyryOfOZ9kEFF8AW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-GyryOfOZ9kEFF8AW .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-GyryOfOZ9kEFF8AW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-GyryOfOZ9kEFF8AW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-GyryOfOZ9kEFF8AW .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-GyryOfOZ9kEFF8AW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-GyryOfOZ9kEFF8AW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-GyryOfOZ9kEFF8AW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-GyryOfOZ9kEFF8AW .marker.cross{stroke:#333333;}#mermaid-svg-GyryOfOZ9kEFF8AW svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-GyryOfOZ9kEFF8AW p{margin:0;}#mermaid-svg-GyryOfOZ9kEFF8AW .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-GyryOfOZ9kEFF8AW .cluster-label text{fill:#333;}#mermaid-svg-GyryOfOZ9kEFF8AW .cluster-label span{color:#333;}#mermaid-svg-GyryOfOZ9kEFF8AW .cluster-label span p{background-color:transparent;}#mermaid-svg-GyryOfOZ9kEFF8AW .label text,#mermaid-svg-GyryOfOZ9kEFF8AW span{fill:#333;color:#333;}#mermaid-svg-GyryOfOZ9kEFF8AW .node rect,#mermaid-svg-GyryOfOZ9kEFF8AW .node circle,#mermaid-svg-GyryOfOZ9kEFF8AW .node ellipse,#mermaid-svg-GyryOfOZ9kEFF8AW .node polygon,#mermaid-svg-GyryOfOZ9kEFF8AW .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-GyryOfOZ9kEFF8AW .rough-node .label text,#mermaid-svg-GyryOfOZ9kEFF8AW .node .label text,#mermaid-svg-GyryOfOZ9kEFF8AW .image-shape .label,#mermaid-svg-GyryOfOZ9kEFF8AW .icon-shape .label{text-anchor:middle;}#mermaid-svg-GyryOfOZ9kEFF8AW .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-GyryOfOZ9kEFF8AW .rough-node .label,#mermaid-svg-GyryOfOZ9kEFF8AW .node .label,#mermaid-svg-GyryOfOZ9kEFF8AW .image-shape .label,#mermaid-svg-GyryOfOZ9kEFF8AW .icon-shape .label{text-align:center;}#mermaid-svg-GyryOfOZ9kEFF8AW .node.clickable{cursor:pointer;}#mermaid-svg-GyryOfOZ9kEFF8AW .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-GyryOfOZ9kEFF8AW .arrowheadPath{fill:#333333;}#mermaid-svg-GyryOfOZ9kEFF8AW .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-GyryOfOZ9kEFF8AW .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-GyryOfOZ9kEFF8AW .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-GyryOfOZ9kEFF8AW .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-GyryOfOZ9kEFF8AW .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-GyryOfOZ9kEFF8AW .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-GyryOfOZ9kEFF8AW .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-GyryOfOZ9kEFF8AW .cluster text{fill:#333;}#mermaid-svg-GyryOfOZ9kEFF8AW .cluster span{color:#333;}#mermaid-svg-GyryOfOZ9kEFF8AW div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-GyryOfOZ9kEFF8AW .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-GyryOfOZ9kEFF8AW rect.text{fill:none;stroke-width:0;}#mermaid-svg-GyryOfOZ9kEFF8AW .icon-shape,#mermaid-svg-GyryOfOZ9kEFF8AW .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-GyryOfOZ9kEFF8AW .icon-shape p,#mermaid-svg-GyryOfOZ9kEFF8AW .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-GyryOfOZ9kEFF8AW .icon-shape .label rect,#mermaid-svg-GyryOfOZ9kEFF8AW .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-GyryOfOZ9kEFF8AW .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-GyryOfOZ9kEFF8AW .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-GyryOfOZ9kEFF8AW :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Yes
No
类型可做 map key?
支持 == 和 != ?
✅ 可以做 key
❌ 不能做 key
切片 \[\]T
mapKV
func
6.3 内存无法主动释放、大 map 内存占用优化
问题 :delete 只标记墓碑或清空 slot,不会释放 group 的内存。map 的内存只在以下情况释放:
- map 变量本身被 GC 回收(没有引用了)
- map 被整体 Clear
优化方案:
go
// 方案1:重建 map(适合大量删除后)
old := m
m = make(map[string]int, len(old)) // 新 map 只有实际元素大小
for k, v := range old {
m[k] = v
}
// old 被 GC 回收
// 方案2:使用 clear(Go 1.21+)
clear(m) // 清空所有元素,保留底层数组
// 方案3:分片 map(减少单 map 的内存压力)
type ShardedMap[K comparable, V any] struct {
shards [64]struct {
mu sync.RWMutex
m map[K]V
}
}
func (s *ShardedMap[K, V]) shard(key K) int {
h := fnv.New32a()
h.Write([]byte(fmt.Sprintf("%v", key)))
return int(h.Sum32() % 64)
}
func (s *ShardedMap[K, V]) Get(key K) (V, bool) {
sh := &s.shards[s.shard(key)]
sh.mu.RLock()
defer sh.mu.RUnlock()
v, ok := sh.m[key]
return v, ok
}
func (s *ShardedMap[K, V]) Set(key K, value V) {
sh := &s.shards[s.shard(key)]
sh.mu.Lock()
defer sh.mu.Unlock()
sh.m[key] = value
}
// 每个 shard 独立 GC,单 shard 扩容不影响其他 shard
大 map 内存占用分析:
每个 entry 的内存开销:
- key 存储:sizeof(key) 或 8 bytes(indirect key 指针)
- elem 存储:sizeof(elem) 或 8 bytes(indirect elem 指针)
- ctrl 字节:1 byte / slot(8 bytes / group)
- group 对齐开销
- table 元数据:~20 bytes / table
- directory:8 bytes * (1 << globalDepth)
示例:map[int64]int64, 100万个元素
- 纯数据:100万 * 16 = 16 MB
- 实际占用:~20 MB(含 ctrl、group 对齐、table 元数据)
- 负载因子 7/8,总 slot 数 = 100万 / (7/8) ≈ 114万
7 总结:Go Map 核心知识图谱
#mermaid-svg-LXVcP08oTcD1eab9{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-LXVcP08oTcD1eab9 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-LXVcP08oTcD1eab9 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-LXVcP08oTcD1eab9 .error-icon{fill:#552222;}#mermaid-svg-LXVcP08oTcD1eab9 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-LXVcP08oTcD1eab9 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-LXVcP08oTcD1eab9 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-LXVcP08oTcD1eab9 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-LXVcP08oTcD1eab9 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-LXVcP08oTcD1eab9 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-LXVcP08oTcD1eab9 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-LXVcP08oTcD1eab9 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-LXVcP08oTcD1eab9 .marker.cross{stroke:#333333;}#mermaid-svg-LXVcP08oTcD1eab9 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-LXVcP08oTcD1eab9 p{margin:0;}#mermaid-svg-LXVcP08oTcD1eab9 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-LXVcP08oTcD1eab9 .cluster-label text{fill:#333;}#mermaid-svg-LXVcP08oTcD1eab9 .cluster-label span{color:#333;}#mermaid-svg-LXVcP08oTcD1eab9 .cluster-label span p{background-color:transparent;}#mermaid-svg-LXVcP08oTcD1eab9 .label text,#mermaid-svg-LXVcP08oTcD1eab9 span{fill:#333;color:#333;}#mermaid-svg-LXVcP08oTcD1eab9 .node rect,#mermaid-svg-LXVcP08oTcD1eab9 .node circle,#mermaid-svg-LXVcP08oTcD1eab9 .node ellipse,#mermaid-svg-LXVcP08oTcD1eab9 .node polygon,#mermaid-svg-LXVcP08oTcD1eab9 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-LXVcP08oTcD1eab9 .rough-node .label text,#mermaid-svg-LXVcP08oTcD1eab9 .node .label text,#mermaid-svg-LXVcP08oTcD1eab9 .image-shape .label,#mermaid-svg-LXVcP08oTcD1eab9 .icon-shape .label{text-anchor:middle;}#mermaid-svg-LXVcP08oTcD1eab9 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-LXVcP08oTcD1eab9 .rough-node .label,#mermaid-svg-LXVcP08oTcD1eab9 .node .label,#mermaid-svg-LXVcP08oTcD1eab9 .image-shape .label,#mermaid-svg-LXVcP08oTcD1eab9 .icon-shape .label{text-align:center;}#mermaid-svg-LXVcP08oTcD1eab9 .node.clickable{cursor:pointer;}#mermaid-svg-LXVcP08oTcD1eab9 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-LXVcP08oTcD1eab9 .arrowheadPath{fill:#333333;}#mermaid-svg-LXVcP08oTcD1eab9 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-LXVcP08oTcD1eab9 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-LXVcP08oTcD1eab9 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-LXVcP08oTcD1eab9 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-LXVcP08oTcD1eab9 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-LXVcP08oTcD1eab9 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-LXVcP08oTcD1eab9 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-LXVcP08oTcD1eab9 .cluster text{fill:#333;}#mermaid-svg-LXVcP08oTcD1eab9 .cluster span{color:#333;}#mermaid-svg-LXVcP08oTcD1eab9 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-LXVcP08oTcD1eab9 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-LXVcP08oTcD1eab9 rect.text{fill:none;stroke-width:0;}#mermaid-svg-LXVcP08oTcD1eab9 .icon-shape,#mermaid-svg-LXVcP08oTcD1eab9 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-LXVcP08oTcD1eab9 .icon-shape p,#mermaid-svg-LXVcP08oTcD1eab9 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-LXVcP08oTcD1eab9 .icon-shape .label rect,#mermaid-svg-LXVcP08oTcD1eab9 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-LXVcP08oTcD1eab9 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-LXVcP08oTcD1eab9 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-LXVcP08oTcD1eab9 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Go Map 核心知识
定义与约束
key必须comparable
Swiss Table底层
Map→table→group→slot
H1/H2哈希分割
并行8slot匹配
probeSeq探测
二次探测序列
墓碑机制
全满group删除
扩容机制
grow 2倍 / split分裂
迭代随机化
entryOffset+dirOffset
并发安全
RWMutex / sync.Map
内存优化
重建/分片/clear
Go 1.26 Swiss Table vs 旧版 hmap/bmap 对比:
旧版 (Go ≤1.23) 新版 (Go 1.24+)
hmap + bmap Map + table + group
拉链法(溢出桶链表) 开放寻址(Swiss Table)
每个 bucket 8个 key/elem 每个 group 8个 slot
overflow bucket 处理冲突 probeSeq 二次探测
等量扩容 + 增量扩容 grow 2倍 + split 分裂
渐进式搬迁(evacuate) 一次性 rehash(单 table 范围小)
evacuated 标记位 table.index = -1(过期标记)
源码索引
文件 关键内容 internal/runtime/maps/map.goMap结构体、NewMap、NewEmptyMap、directoryIndex、installTableSplit、replaceTableinternal/runtime/maps/table.gotable结构体、Get、PutSlot、Delete、rehash、grow、split、Iter、probeSeqinternal/runtime/maps/group.goctrlGroup、matchH2、matchEmpty、matchFull、groupReference、bitsetruntime/map.gomakemap、makemap_small、mapassign、mapaccess1/2、mapdelete、mapIterStart/Next、mapclear