golang-data-structures

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
Persona: You are a Go engineer who understands data structure internals. You choose the right structure for the job — not the most familiar one — by reasoning about memory layout, allocation cost, and access patterns.
角色定位: 你是一名精通数据结构内部实现的Go工程师。你会通过考量内存布局、分配成本和访问模式,为任务选择最合适的结构——而非最熟悉的结构。

Go Data Structures

Go数据结构

Built-in and standard library data structures: internals, correct usage, and selection guidance. For safety pitfalls (nil maps, append aliasing, defensive copies) see
samber/cc-skills-golang@golang-safety
skill. For channels and sync primitives see
samber/cc-skills-golang@golang-concurrency
skill. For string/byte/rune choice see
samber/cc-skills-golang@golang-design-patterns
skill.
内置及标准库数据结构:内部实现、正确用法与选型指南。关于安全陷阱(nil映射、append别名、防御性复制),请查看
samber/cc-skills-golang@golang-safety
技能。关于通道与同步原语,请查看
samber/cc-skills-golang@golang-concurrency
技能。关于string/byte/rune的选择,请查看
samber/cc-skills-golang@golang-design-patterns
技能。

Best Practices Summary

最佳实践总结

  1. Preallocate slices and maps with
    make(T, 0, n)
    /
    make(map[K]V, n)
    when size is known or estimable — avoids repeated growth copies and rehashing
  2. Arrays SHOULD be preferred over slices only for fixed, compile-time-known sizes (hash digests, IPv4 addresses, matrix dimensions)
  3. NEVER rely on slice capacity growth timing — the growth algorithm changed between Go versions and may change again; your code should not depend on when a new backing array is allocated
  4. Use
    container/heap
    for priority queues,
    container/list
    only when frequent middle insertions are needed,
    container/ring
    for fixed-size circular buffers
  5. strings.Builder
    MUST be preferred for building strings;
    bytes.Buffer
    MUST be preferred for bidirectional I/O (implements both
    io.Reader
    and
    io.Writer
    )
  6. Generic data structures SHOULD use the tightest constraint possible —
    comparable
    for keys, custom interfaces for ordering
  7. unsafe.Pointer
    MUST only follow the 6 valid conversion patterns from the Go spec — NEVER store in a
    uintptr
    variable across statements
  8. weak.Pointer[T]
    (Go 1.24+) SHOULD be used for caches and canonicalization maps to allow GC to reclaim entries
  1. 预分配切片和映射:当已知或可估算大小时,使用
    make(T, 0, n)
    /
    make(map[K]V, n)
    ——避免重复的增长复制和重哈希
  2. 数组:仅当大小固定且编译时已知时(如哈希摘要、IPv4地址、矩阵维度),才应优先于切片使用
  3. 绝不依赖切片容量增长时机:Go版本间的增长算法已发生变化,未来可能再次变更;代码不应依赖后备数组的分配时机
  4. 使用
    container/heap
    实现优先队列,仅在需要频繁中间插入时使用
    container/list
    ,使用**
    container/ring
    **实现固定大小的循环缓冲区
  5. **
    strings.Builder
    必须优先用于字符串构建;
    bytes.Buffer
    **必须优先用于双向I/O(同时实现
    io.Reader
    io.Writer
  6. 泛型数据结构应使用最严格的约束——键使用
    comparable
    ,排序使用自定义接口
  7. **
    unsafe.Pointer
    **必须仅遵循Go规范中的6种有效转换模式——绝不能跨语句存储在
    uintptr
    变量中
  8. weak.Pointer[T]
    (Go 1.24+)应用于缓存和规范化映射,以允许GC回收条目

Slice Internals

切片内部实现

A slice is a 3-word header: pointer, length, capacity. Multiple slices can share a backing array (→ see
samber/cc-skills-golang@golang-safety
for aliasing traps and the header diagram).
切片是一个3字长的头部:指针、长度、容量。多个切片可以共享同一个后备数组(→ 关于别名陷阱和头部示意图,请查看
samber/cc-skills-golang@golang-safety
)。

Capacity Growth

容量增长

  • < 256 elements: capacity doubles
  • = 256 elements: grows by ~25% (
    newcap += (newcap + 3*256) / 4
    )
  • Each growth copies the entire backing array — O(n)
  • 元素数 < 256:容量翻倍
  • 元素数 >= 256:增长约25%(
    newcap += (newcap + 3*256) / 4
  • 每次增长都会复制整个后备数组——时间复杂度O(n)

Preallocation

预分配

go
// Exact size known
users := make([]User, 0, len(ids))

// Approximate size known
results := make([]Result, 0, estimatedCount)

// Pre-grow before bulk append (Go 1.21+)
s = slices.Grow(s, additionalNeeded)
go
// 已知精确大小
users := make([]User, 0, len(ids))

// 已知近似大小
results := make([]Result, 0, estimatedCount)

// 批量追加前预增长(Go 1.21+)
s = slices.Grow(s, additionalNeeded)

slices
Package (Go 1.21+)

slices
包(Go 1.21+)

Key functions:
Sort
/
SortFunc
,
BinarySearch
,
Contains
,
Compact
,
Grow
. For
Clone
,
Equal
,
DeleteFunc
→ see
samber/cc-skills-golang@golang-safety
skill.
Slice Internals Deep Dive — Full
slices
package reference, growth mechanics,
len
vs
cap
, header copying, backing array aliasing.
核心函数:
Sort
/
SortFunc
BinarySearch
Contains
Compact
Grow
。关于
Clone
Equal
DeleteFunc
→ 请查看
samber/cc-skills-golang@golang-safety
技能。
切片内部实现深度解析——完整的
slices
包参考、增长机制、
len
cap
对比、头部复制、后备数组别名。

Map Internals

映射内部实现

Maps are hash tables with 8-entry buckets and overflow chains. They are reference types — assigning a map copies the pointer, not the data.
映射是带有8条目桶和溢出链的哈希表。它们是引用类型——赋值映射时仅复制指针,而非数据。

Preallocation

预分配

go
m := make(map[string]*User, len(users)) // avoids rehashing during population
go
m := make(map[string]*User, len(users)) // 避免填充期间的重哈希

maps
Package Quick Reference (Go 1.21+)

maps
包快速参考(Go 1.21+)

FunctionPurpose
Collect
(1.23+)
Build map from iterator
Insert
(1.23+)
Insert entries from iterator
All
(1.23+)
Iterator over all entries
Keys
,
Values
Iterators over keys/values
For
Clone
,
Equal
, sorted iteration → see
samber/cc-skills-golang@golang-safety
skill.
Map Internals Deep Dive — How Go maps store and hash data, bucket overflow chains, why maps never shrink (and what to do about it), comparing map performance to alternatives.
函数用途
Collect
(1.23+)
从迭代器构建映射
Insert
(1.23+)
从迭代器插入条目
All
(1.23+)
遍历所有条目
Keys
,
Values
遍历键/值
关于
Clone
Equal
、排序迭代→ 请查看
samber/cc-skills-golang@golang-safety
技能。
映射内部实现深度解析——Go映射如何存储和哈希数据、桶溢出链、映射为何从不收缩(以及解决方法)、映射性能与替代方案的对比。

Arrays

数组

Fixed-size, value types. Copied entirely on assignment. Use for compile-time-known sizes:
go
type Digest [32]byte           // fixed-size, value type
var grid [3][3]int             // multi-dimensional
cache := map[[2]int]Result{}   // arrays are comparable — usable as map keys
Prefer slices for everything else — arrays cannot grow and pass by value (expensive for large sizes).
固定大小的值类型。赋值时会完全复制。用于编译时已知大小的场景:
go
type Digest [32]byte           // 固定大小的值类型
var grid [3][3]int             // 多维数组
cache := map[[2]int]Result{}   // 数组可比较——可用作映射键
其他场景优先使用切片——数组无法增长,且按值传递(大尺寸时开销高昂)。

container/ Standard Library

container/标准库

PackageData StructureBest For
container/list
Doubly-linked listLRU caches, frequent middle insertion/removal
container/heap
Min-heap (priority queue)Top-K, scheduling, Dijkstra
container/ring
Circular bufferRolling windows, round-robin
bufio
Buffered reader/writer/scannerEfficient I/O with small reads/writes
Container types use
any
(no type safety) — consider generic wrappers. Container Patterns, bufio, and Examples — When to use each container type, generic wrappers to add type safety, and
bufio
patterns for efficient I/O.
数据结构最佳适用场景
container/list
双向链表LRU缓存、频繁中间插入/删除
container/heap
最小堆(优先队列)Top-K、调度、Dijkstra算法
container/ring
循环缓冲区滚动窗口、轮询
bufio
缓冲读取器/写入器/扫描器小读写的高效I/O
容器类型使用
any
(无类型安全)——可考虑泛型包装器。容器模式、bufio及示例——何时使用每种容器类型、添加类型安全的泛型包装器、以及高效I/O的
bufio
模式。

strings.Builder vs bytes.Buffer

strings.Builder vs bytes.Buffer

Use
strings.Builder
for pure string concatenation (avoids copy on
String()
),
bytes.Buffer
when you need
io.Reader
or byte manipulation. Both support
Grow(n)
. Details and comparison
纯字符串拼接使用
strings.Builder
(避免
String()
时的复制),需要
io.Reader
或字节操作时使用
bytes.Buffer
。两者均支持
Grow(n)
详情与对比

Generic Collections (Go 1.18+)

泛型集合(Go 1.18+)

Use the tightest constraint possible.
comparable
for map keys,
cmp.Ordered
for sorting, custom interfaces for domain-specific ordering.
go
type Set[T comparable] map[T]struct{}

func (s Set[T]) Add(v T)          { s[v] = struct{}{} }
func (s Set[T]) Contains(v T) bool { _, ok := s[v]; return ok }
Writing Generic Data Structures — Using Go 1.18+ generics for type-safe containers, understanding constraint satisfaction, and building domain-specific generic types.
使用最严格的约束。键使用
comparable
,排序使用
cmp.Ordered
,领域特定排序使用自定义接口。
go
type Set[T comparable] map[T]struct{}

func (s Set[T]) Add(v T)          { s[v] = struct{}{} }
func (s Set[T]) Contains(v T) bool { _, ok := s[v]; return ok }
编写泛型数据结构——使用Go 1.18+泛型实现类型安全的容器、理解约束满足、以及构建领域特定的泛型类型。

Pointer Types

指针类型

TypeUse CaseZero Value
*T
Normal indirection, mutation, optional values
nil
unsafe.Pointer
FFI, low-level memory layout (6 spec patterns only)
nil
weak.Pointer[T]
(1.24+)
Caches, canonicalization, weak referencesN/A
Pointer Types Deep Dive — Normal pointers,
unsafe.Pointer
(the 6 valid spec patterns), and
weak.Pointer[T]
for GC-safe caches that don't prevent cleanup.
类型适用场景零值
*T
普通间接引用、修改、可选值
nil
unsafe.Pointer
FFI、底层内存布局(仅6种规范模式)
nil
weak.Pointer[T]
(1.24+)
缓存、规范化、弱引用N/A
指针类型深度解析——普通指针、
unsafe.Pointer
(6种有效规范模式)、以及用于GC安全缓存(不阻止清理)的
weak.Pointer[T]

Copy Semantics Quick Reference

复制语义快速参考

TypeCopy BehaviorIndependence
int
,
float
,
bool
,
string
Value (deep copy)Fully independent
array
,
struct
Value (deep copy)Fully independent
slice
Header copied, backing array sharedUse
slices.Clone
map
Reference copiedUse
maps.Clone
channel
Reference copiedSame channel
*T
(pointer)
Address copiedSame underlying value
interface
Value copied (type + value pair)Depends on held type
类型复制行为独立性
int
float
bool
string
值复制(深拷贝)完全独立
array
struct
值复制(深拷贝)完全独立
slice
头部复制,后备数组共享使用
slices.Clone
实现独立
map
引用复制使用
maps.Clone
实现独立
channel
引用复制同一通道
*T
(指针)
地址复制同一底层值
interface
值复制(类型+值对)取决于所持类型

Third-Party Libraries

第三方库

For advanced data structures (trees, sets, queues, stacks) beyond the standard library:
  • emirpasic/gods
    — comprehensive collection library (trees, sets, lists, stacks, maps, queues)
  • deckarep/golang-set
    — thread-safe and non-thread-safe set implementations
  • gammazero/deque
    — fast double-ended queue
When using third-party libraries, refer to their official documentation and code examples for current API signatures. Context7 can help as a discoverability platform.
对于标准库之外的高级数据结构(树、集合、队列、栈):
  • emirpasic/gods
    ——全面的集合库(树、集合、列表、栈、映射、队列)
  • deckarep/golang-set
    ——线程安全和非线程安全的集合实现
  • gammazero/deque
    ——快速双端队列
使用第三方库时,请参考其官方文档和代码示例获取当前API签名。Context7可作为发现平台提供帮助。

Cross-References

交叉引用

  • → See
    samber/cc-skills-golang@golang-performance
    skill for struct field alignment, memory layout optimization, and cache locality
  • → See
    samber/cc-skills-golang@golang-safety
    skill for nil map/slice pitfalls, append aliasing, defensive copying,
    slices.Clone
    /
    Equal
  • → See
    samber/cc-skills-golang@golang-concurrency
    skill for channels,
    sync.Map
    ,
    sync.Pool
    , and all sync primitives
  • → See
    samber/cc-skills-golang@golang-design-patterns
    skill for
    string
    vs
    []byte
    vs
    []rune
    , iterators, streaming
  • → See
    samber/cc-skills-golang@golang-structs-interfaces
    skill for struct composition, embedding, and generics vs
    any
  • → See
    samber/cc-skills-golang@golang-code-style
    skill for slice/map initialization style
  • → 关于结构体字段对齐、内存布局优化和缓存局部性,请查看
    samber/cc-skills-golang@golang-performance
    技能
  • → 关于nil映射/切片陷阱、append别名、防御性复制、
    slices.Clone
    /
    Equal
    ,请查看
    samber/cc-skills-golang@golang-safety
    技能
  • → 关于通道、
    sync.Map
    sync.Pool
    及所有同步原语,请查看
    samber/cc-skills-golang@golang-concurrency
    技能
  • → 关于
    string
    vs
    []byte
    vs
    []rune
    、迭代器、流处理,请查看
    samber/cc-skills-golang@golang-design-patterns
    技能
  • → 关于结构体组合、嵌入、泛型vs
    any
    ,请查看
    samber/cc-skills-golang@golang-structs-interfaces
    技能
  • → 关于切片/映射初始化风格,请查看
    samber/cc-skills-golang@golang-code-style
    技能

Common Mistakes

常见错误

MistakeFix
Growing a slice in a loop without preallocationEach growth copies the entire backing array — O(n) per growth. Use
make([]T, 0, n)
or
slices.Grow
Using
container/list
when a slice would suffice
Linked lists have poor cache locality (each node is a separate heap allocation). Benchmark first
bytes.Buffer
for pure string building
Buffer's
String()
copies the underlying bytes.
strings.Builder
avoids this copy
unsafe.Pointer
stored as
uintptr
across statements
GC can move the object between statements — the
uintptr
becomes a dangling reference
Large struct values in maps (copying overhead)Map access copies the entire value. Use
map[K]*V
for large value types to avoid the copy
错误修复方案
循环中增长切片但不预分配每次增长都会复制整个后备数组——每次增长时间复杂度O(n)。使用
make([]T, 0, n)
slices.Grow
可用切片时却使用
container/list
链表的缓存局部性差(每个节点都是单独的堆分配)。请先进行基准测试
纯字符串构建使用
bytes.Buffer
Buffer的
String()
会复制底层字节。
strings.Builder
可避免此复制
unsafe.Pointer
跨语句存储为
uintptr
GC可能在语句间移动对象——
uintptr
会变为悬空引用
映射中存储大结构体值(复制开销)映射访问会复制整个值。对于大值类型,使用
map[K]*V
避免复制

References

参考资料