D
AI
学习工作台
8 周后端冲刺2026-05-222 分钟阅读

Go slice 与 map

底层数组、扩容机制、map 无序与并发安全

8周冲刺week2Goslicemap记笔记标记疑惑

slice:动态数组视图

Go 的 slice 是对 底层数组 的引用。理解 slice 是后端面试必考点,与 Week 1 数组复杂度理论直接对应。

a := []int{1, 2, 3}
b := a[1:3]   // len=2, cap=2, 共享底层
b[0] = 99     // a 变为 [1, 99, 3]

截取s[low:high:max] 三索引形式可限制 cap,避免 append 覆盖意外区域。

append 与扩容

s := make([]int, 0, 4)
s = append(s, 1, 2, 3)
s2 := append(s, 4)     // 可能仍用原数组
s3 := append(s, 5)     // 若 cap 满,新数组,s 不变

面试说明:扩容后 新 slice 可能与旧 slice 脱钩;大 slice append 注意预分配 make([]T, 0, n) 减少拷贝。

常见陷阱

  • for range 取地址for _, v := range s { ptrs = append(ptrs, &v) } 全指向同一变量。
  • nil slice vs empty slicevar s []ints := []int{} JSON 序列化行为不同。
  • 删除元素s = append(s[:i], s[i+1:]...) 不释放内存,大 buffer 可 copy 到新 slice。
  • map internals(概念)

    map 是哈希表:桶 + 溢出链。键可比较(==),无序 遍历顺序随机。

    m := make(map[string]int, 100) // 预容量减少 rehash
    v, ok := m["key"]              // ok 表示是否存在
    delete(m, "key")

    零值 map 不能写var m map[string]int; m["a"]=1 panic,需 make

    并发安全

    • 普通 map:非线程安全
    • sync.Map:读多写少、键稳定场景;否则 RWMutex + map。
    • 只读 map 初始化后多 goroutine 读:需保证无写(或 immutable copy)。

    slice 与 map 面试题

    • 如何用 slice 实现栈/队列?
    • map 键能否是 slice?不能,slice 不可比较。
    • 遍历 map 删除:先收集 key 再删,或 range 中 break 策略。

    与算法联系

    两数之和、前缀和在 Go 里用 map[int]int。Week 2 并发篇 week02-go-goroutine-channel 会讲多个 goroutine 写 map 的正确模式。

    实践

    reflect.SliceHeader 概念(Go 1.20+ 不推荐滥用 unsafe)。写小 benchmark 对比预分配 vs 默认 append。结合 /chapters/algorithm-lab 用 Go 刷 2 道哈希题。

    实战巩固与面试表达

    本篇属于 8 周冲刺 week02-go-slice-map 主题。复习时先闭卷回答 frontmatter 中三张 flashcard,再展开口述两个「为什么」:为什么这种方案能 work、边界失败时如何降级。与相邻章节对照:算法篇强调复杂度与模板,Go 篇强调工程默认写法,中间件篇强调线上故障案例。

    动手与自检清单

    用 25 分钟限时做 1 道相关练习题或画出一张架构/数据结构示意图;用 5 分钟写 STAR 片段说明你在项目里是否用过类似技术。记录 3 个面试追问及你的标准答法,存入 /zh/notebook/master-plan 笔记。若某点不熟,回到对应 /chapters 交互 Lab 重新走一遍流程,比死记卡片更有效。

    易错点提醒

    避免只背名词不会画图;避免只说优点不谈 trade-off(性能、一致性、运维成本至少提一项);避免把学习 Demo 说成百万 QPS 生产。回答时使用「场景 → 方案 → 结果 → 反思」四段式,体现工程成熟度。

    知识卡片

    问题

    slice 的三元组是什么?

    点击翻转查看答案

    答案

    指针指向底层数组、len 长度、cap 容量。slice 是描述符,赋值 slice 共享底层数组(浅拷贝)。

    问题

    append 何时触发扩容?

    点击翻转查看答案

    答案

    len+1 > cap 时分配新数组(通常约 2 倍或按 Go 版本策略增长),拷贝旧元素;旧 slice 仍指向原数组。

    问题

    map 并发读写会怎样?

    点击翻转查看答案

    答案

    未加锁并发写或读写会 fatal error;需 mutex、sync.Map 或每个 goroutine 本地 map 再合并。