Skip to content

切片与 Map

slice 扩容与底层数组陷阱、map 的 hmap、遍历随机性、并发读写。

Updated View as Markdown
For humans

切片与 Map

slice 和 map 是 Go 最常用的容器,也是考点最密集的区域:底层结构、扩容、共享陷阱、并发安全

slice:三字段视图

s := make([]int, 3, 5)   // len=3, cap=5

slice 是“视图”结构:ptr(底层数组指针)+ len + cap。复制 slice 只复制视图,底层数组共享。

扩容机制

  • 追加超过 cap 时扩容,分配新数组并拷贝
  • 容量增长:1.18 前小切片双倍,1.18 后小于 256 双倍、之后约 1.25 倍
  • 扩容后是新数组:旧 slice 和扩容后的 slice 不再共享

共享陷阱: 视图共享数组, 扩容才分离

经典陷阱

a := []int{1, 2, 3, 4}
b := a[:2]        // b = [1 2], 共享数组
b = append(b, 99) // 有剩余容量: 写的是共享数组! a 变成 [1 2 99 4]

面试必答:append 在 cap 内时写共享数组,超 cap 才复制。函数传 slice 修改元素会反映到调用方,但 append 改变 len 不会。

map:hmap 结构

map 底层: 哈希桶 + 溢出链

  • hmap:count、桶数组指针、B(桶数 2^B)、扩容状态
  • bucket:8 个键值槽 + 溢出桶指针(冲突链)
  • 查找:hash(key) → 定位桶 → 槽内比较(tophash 加速)
  • 扩容:负载因子超 6.5 或溢出桶过多;渐进式(每次操作搬一部分)

遍历随机性:for range map 的顺序是随机的(语言保证随机起点,防依赖顺序的 bug)。需要有序遍历:排序 key。

并发读写(必考):map 不是并发安全的。并发写或读写并发直接 fatal error: concurrent map writes(不是 panic,不可 recover)。解法:sync.Mutex 包一层、sync.Map(读多写少场景)或分片锁。

面试追问

  1. slice 扩容规则? 超过 cap 分配新数组拷贝。小切片双倍,大切片 1.25 倍(1.18+)。扩容后不共享
  2. append 的共享陷阱? cap 内 append 写共享数组,影响所有共享视图。超 cap 才分离
  3. map 为什么遍历无序? 语言层面随机化,防依赖顺序。要有序自己排序 key
  4. map 并发写会怎样? fatal error(不可 recover)。加锁或 sync.Map
  5. map 扩容机制? 负载因子超阈值渐进式扩容:每次操作搬部分桶,避免一次性卡顿
Navigation

Type to search…

↑↓ navigate↵ selectEsc close