Go 语言切片(Slice)

底层结构、append 扩容、三索引切片、以及共享底层数组的坑。

Intermediate
Key Points切片是「指针 + 长度 + 容量」的三元组,`append` 超容量才重新分配。

底层结构

Go
type slice struct {
	array unsafe.Pointer    // 指向底层数组
	len   int               // 当前长度
	cap   int               // 容量
}

len is how many elements you can access; cap is the space from the start to the end of the backing array. Understanding the two explains every "weird" slice behaviour.

创建

Go
var a []int                     // nil 切片,len=0 cap=0
b := []int{1, 2, 3}
c := make([]int, 3)             // len=3 cap=3,全 0
d := make([]int, 2, 5)          // len=2 cap=5
e := a[0:0]                     // 空切片但非 nil

fmt.Println(a == nil, e == nil)
fmt.Println(len(b), len(c), cap(c), len(d), cap(d))
Output
true false
3 3 3 2 5

append 与扩容

Go
s := make([]int, 0, 2)
fmt.Println(len(s), cap(s))     // 0 2

s = append(s, 1)
fmt.Println(len(s), cap(s))     // 1 2

s = append(s, 2)
fmt.Println(len(s), cap(s))     // 2 2

s = append(s, 3)                // 超容量,重新分配
fmt.Println(len(s), cap(s))     // 3 4

s = append(s, 4, 5, 6, 7, 8)
fmt.Println(len(s), cap(s))     // 8 8
Output
0 2
1 2
2 2
3 4
8 8

Growth policy shifts slightly between versions: roughly double below 256, ~1.25× above. Growth copies every element, so pre-allocate with make([]T, 0, n) when the size is known.

共享底层数组的坑

Go
a := []int{1, 2, 3, 4, 5}
b := a[1:3]                     // len=2 cap=4,共享底层数组

b[0] = 99
fmt.Println(a)                  // a 也变了!

b = append(b, 100)              // 容量够,直接写进 a 的位置
fmt.Println(a)
Output
[1 99 3 4 5]
[1 99 3 100 5]
Slicing does not copy. For an independent copy use slices.Clone(a[1:3]) (Go 1.21+) or append([]int(nil), a[1:3]...). Most often hit when parsing packets or paging data.

三索引切片

Go
a := []int{1, 2, 3, 4, 5}
b := a[1:3:3]                   // 第三个数字限制 cap
fmt.Println(len(b), cap(b))     // 2 2

b = append(b, 100)              // 必须重新分配,不影响 a
fmt.Println(a)
fmt.Println(b)
Output
2 2
[1 2 3 4 5]
[2 3 100]

a[low:high:max] sets cap = max - low. Putting a wall in front of append is a common way for libraries to stop callers mutating internal data.

常用操作

Go
s := []int{1, 2, 3, 4, 5}

// 删除下标 2
s = append(s[:2], s[3:]...)
fmt.Println(s)

// 在中间插入
s = append(s[:1], append([]int{99}, s[1:]...)...)
fmt.Println(s)

// 复制
dst := make([]int, len(s))
n := copy(dst, s)
fmt.Println(n, dst)

// 反转
slices.Reverse(s)
fmt.Println(s)
Output
[1 2 4 5]
[1 99 2 4 5]
5 [1 99 2 4 5]
[5 4 2 99 1]
FunctionUse
append追加
copy复制,返回复制的个数
slices.Clone独立副本
slices.Contains是否包含
slices.Index找下标
slices.Sort排序
slices.Reverse反转

Friends & Partners