Go 语言切片(Slice)¶
切片是对底层数组某个连续区间的描述符,包含指针、长度 len 和容量 cap。切片长度可变,
但切片本身不存储元素;多个切片可能共享同一个底层数组。
创建与追加¶
package main
import "fmt"
func main() {
a := []int{1, 2, 3}
b := make([]int, 2, 5)
b = append(b, 30, 40)
fmt.Println(a, len(a), cap(a))
fmt.Println(b, len(b), cap(b))
}
make([]int, 2, 5) 创建长度为 2、容量为 5 的切片,前两个元素已存在且为零值。
append 返回新的切片描述符,必须接收返回值。容量不足时它会分配新底层数组并复制旧元素,
容量增长策略属于实现细节,代码不应依赖具体倍数。
截取与共享底层数组¶
这是切片与数组复制的第一个关键差异:切片截取不复制元素,part 与 base 共享数据。
如果需要独立副本,可使用 slices.Clone 或 copy:
三索引表达式可限制容量,避免被调用方追加时覆盖原切片的后续元素:
nil 切片与空切片¶
var nilSlice []int
empty := []int{}
fmt.Println(nilSlice == nil, len(nilSlice)) // true 0
fmt.Println(empty == nil, len(empty)) // false 0
两者都可 range、append,长度都为 0,这是第二个差异示例。它们在 JSON 编码等边界上可能分别
表现为 null 与 [],API 契约若区分二者就要显式规范。切片不能互相使用 == 比较;Go 1.21+
可使用标准库 slices.Equal 比较可比较元素。
删除与清理¶
values := []int{1, 2, 3, 4}
i := 1
values = append(values[:i], values[i+1:]...)
clear(values[len(values):cap(values)])
删除会移动后续元素。若元素含指针且底层数组长期存活,应清零不再使用的槽位,避免对象被意外保留。
Go 1.21+ 的 clear 可清零切片元素。
常见错误¶
- 忽略
append返回值,导致长度未更新或新数组丢失。 - 认为子切片已经复制,修改后意外污染原数据。
- 返回一个很小的子切片,却让超大底层数组长期无法被 GC 回收。
- 混淆长度和容量,访问
s[len(s)]会越界,即使容量仍有空间。 - 在多个 goroutine 中无同步地追加或修改同一切片,产生数据竞争。
性能与工程实践¶
已知大致元素数时用 make([]T, 0, n) 预分配容量,可减少扩容和复制,但过度预分配会浪费内存。
跨 API 边界时应说明函数是否保留、修改或异步使用传入切片;所有权不清比单次复制成本更危险。
需要长期保存小窗口时应克隆数据。排序、比较、查找和删除优先考虑标准库 slices 包,避免重复实现。