| name | go-data-structures |
| description | Use when working with Go slices, maps, or arrays — choosing between new and make, using append, declaring empty slices (nil vs literal for JSON), implementing sets with maps, and copying data at boundaries. Also use when building or manipulating collections, even if the user doesn't ask about allocation idioms. Does not cover concurrent data structure safety (see go-concurrency). |
| license | Apache-2.0 |
| metadata | {"sources":"Effective Go, Google Style Guide, Uber Style Guide, Go Wiki CodeReviewComments"} |
Go 数据结构
选择数据结构
你需要什么?
├─ 有序的元素集合
│ ├─ 编译时已知固定大小 → 数组 [N]T
│ └─ 动态大小 → 切片 []T
│ ├─ 知道大概的大小?→ make([]T, 0, capacity)
│ └─ 未知大小或需要 nil 安全的 JSON?→ var s []T (nil)
├─ 键值查找
│ └─ 映射 map[K]V
│ ├─ 知道大概的大小?→ make(map[K]V, capacity)
│ └─ 需要集合?→ map[T]struct{}(零大小值)
└─ 需要传递给函数?
└─ 如果调用者可能会修改它,则在边界处复制
此技能不适用的场景:对于数据结构的并发访问(互斥锁、原子操作),参见 go-concurrency。对于 API 边界处的防御性复制,参见 go-defensive。对于为性能预分配容量,参见 go-performance。
切片
append 函数
始终赋值结果 — 底层数组可能会改变:
x := []int{1, 2, 3}
x = append(x, 4, 5, 6)
x = append(x, y...)
二维切片
独立的内部切片(可以独立增长/缩小):
picture := make([][]uint8, YSize)
for i := range picture {
picture[i] = make([]uint8, XSize)
}
单次分配(对于固定大小更高效):
picture := make([][]uint8, YSize)
pixels := make([]uint8, XSize*YSize)
for i := range picture {
picture[i], pixels = pixels[:XSize], pixels[XSize:]
}
在调试意外的切片行为、跨 goroutine 共享切片或处理切片头时,阅读 references/SLICES.md。
声明空切片
优先使用 nil 切片而非空字面量:
var t []string
t := []string{}
两者的 len 和 cap 都是零,但 nil 切片是首选风格。
JSON 例外:nil 切片编码为 null,而 []string{} 编码为 []。当需要 JSON 数组时使用非 nil。
在设计接口时,避免区分 nil 和非 nil 的零长度切片。
映射
实现集合
使用 map[T]bool — 惯用且阅读自然:
attended := map[string]bool{"Ann": true, "Joe": true}
if attended[person] {
fmt.Println(person, "was at the meeting")
}
复制
从另一个包复制结构体时要小心。如果类型的方法定义在指针类型(*T)上,复制值可能导致别名 bug。
通用规则: 如果类型 T 的方法与指针类型 *T 关联,则不要复制 T 的值。这适用于 bytes.Buffer、sync.Mutex、sync.WaitGroup 以及包含它们的类型。
var mu sync.Mutex
mu2 := mu
func increment(sc *SafeCounter) {
sc.mu.Lock()
sc.count++
sc.mu.Unlock()
}
快速参考
| 主题 | 关键点 |
|---|
| 切片 | 始终赋值 append 结果;nil 切片优于 []T{} |
| 集合 | map[T]bool 是惯用写法 |
| 复制 | 如果方法在 *T 上则不要复制 T;注意别名问题 |
相关技能