切片操作
本教程共 80 篇 · 第 29 篇 · 更新于 2026-07-27 · 约 8 分钟阅读
29. 切片操作
本节目标:学会用 append 往切片加元素,用切片表达式截取片段,用 copy 复制切片,以及怎么删掉不需要的元素。
append:往切片加东西
append 是切片最常用的操作,往尾部追加元素:
package main
import "fmt"
func main() {
s := []int{1, 2, 3}
s = append(s, 4)
fmt.Println(s) // [1 2 3 4]
// 一次加多个
s = append(s, 5, 6, 7)
fmt.Println(s) // [1 2 3 4 5 6 7]
// 追加另一个切片(用...展开)
s2 := []int{8, 9}
s = append(s, s2...)
fmt.Println(s) // [1 2 3 4 5 6 7 8 9]
}
注意 append 的返回值必须接住。为什么?因为切片可能需要扩容,扩容后会分配新的底层数组,原来的切片指针就失效了。
// 错误写法
append(s, 4) // s 没变!返回值丢了
// 正确写法
s = append(s, 4)
Warning
append的第一个参数可能是 nil 切片,这完全没问题。append会自动创建底层数组。
切片表达式:截取片段
用 s[low:high] 可以截取切片的一部分,和从数组创建切片的语法一样:
s := []int{1, 2, 3, 4, 5}
fmt.Println(s[1:3]) // [2 3] 下标1到3(不含3)
fmt.Println(s[:3]) // [1 2 3] 从头到下标3
fmt.Println(s[2:]) // [3 4 5] 从下标2到末尾
fmt.Println(s[:]) // [1 2 3 4 5] 全部
这种切片操作和原切片共享底层数组。改了新的,旧的也会变:
s := []int{1, 2, 3, 4, 5}
sub := s[1:3]
sub[0] = 100
fmt.Println(s) // [1 100 3 4 5] s 也变了!
fmt.Println(sub) // [100 3]
三索引切片表达式
Go 1.2 引入了三索引切片表达式 s[low:high:max],用来限制新切片的容量:
s := []int{1, 2, 3, 4, 5}
sub := s[1:3:4]
fmt.Println(sub) // [2 3]
fmt.Println(len(sub)) // 2
fmt.Println(cap(sub)) // 3 (max - low = 4 - 1 = 3)
第三个参数 max 限制了新切片的容量上限。这样做的好处是:如果你不希望新切片通过 append 影响到原切片后面的元素,用三索引就能切断这个关联。
Note三索引表达式必须满足
low <= high <= max <= cap(s),否则会 panic。
copy:复制切片
如果你想要一个完全独立的副本,用 copy:
package main
import "fmt"
func main() {
src := []int{1, 2, 3}
dst := make([]int, 3)
n := copy(dst, src)
fmt.Println(dst) // [1 2 3]
fmt.Println(n) // 3 返回复制的元素个数
// 改副本不影响原件
dst[0] = 100
fmt.Println(src) // [1 2 3]
}
copy 返回实际复制的元素个数,取 src 和 dst 长度的较小值。
Tip
copy也可以用来在同一个切片内部搬数据,比如把后面的元素往前挪。src 和 dst 可以重叠。
删除元素
Go 的切片没有内置的 remove 方法。删除元素需要你自己用 append 来做。
删除下标 i 的元素,思路是:把 i 后面的元素往前挪一位,然后截掉最后一个:
package main
import "fmt"
func main() {
s := []int{1, 2, 3, 4, 5}
i := 2 // 要删除下标2的元素(值是3)
s = append(s[:i], s[i+1:]...)
fmt.Println(s) // [1 2 4 5]
}
拆开看就是三步:
s[:i]取删除点前面的部分:[1 2]s[i+1:]...把删除点后面的部分展开追加进去:[4 5]- append 把两部分拼起来就是
[1 2 4 5]
Warning这种删除方式有个问题:如果切片里有指针或包含指针的结构体,被「删除」的元素其实还在底层数组里,可能导致内存泄漏。更安全的做法是先显式置 nil:
s := []*int{...}
i := 2
s[i] = nil // 先释放引用
s = append(s[:i], s[i+1:]...)
在中间插入元素
append 只能往尾部加。要在中间插入,也是手动拼:
package main
import "fmt"
func main() {
s := []int{1, 2, 4, 5}
i := 2 // 在下标2处插入
// 先追加一个空位
s = append(s, 0)
// 把 i 及之后的元素往后挪一位
copy(s[i+1:], s[i:])
// 在 i 处放入新值
s[i] = 3
fmt.Println(s) // [1 2 3 4 5]
}
虽然步骤多了点,但逻辑很清晰。
小结
切片操作的核心就四个:
append加元素,返回值必须接住s[low:high]截取片段,注意共享底层数组copy做独立复制- 删除靠
append+ 切片表达式手动拼
这些操作写多了就熟练了。下一章讲切片扩容的内部机制,搞懂 cap 为什么会「跳跃式增长」。