用append拼接子切片实现删除:先校验索引有效性,再执行nums = append(nums[:i], nums[i+1:]...),注意末尾元素和空切片的合法处理,复用底层数组时需手动置零防内存泄漏。

用 append 覆盖原切片实现“删除”
Go 没有内置的 delete 函数用于切片,所谓“删除”本质是构造一个新底层数组(或复用部分旧空间)并重新赋值给原变量。最常用且安全的方式是用 append 拼接被删元素前后的子切片:
nums := []int{1, 2, 3, 4, 5}
i := 2 // 删除索引为 2 的元素(即 3)
nums = append(nums[:i], nums[i+1:]...)
这行代码会把 nums[:i]([1 2])和 nums[i+1:]([4 5])拼成新切片 [1 2 4 5],再赋给 nums。
- 必须加
...展开右侧切片,否则append会把整个切片当单个参数传入,编译报错:cannot use nums[i+1:] (type []int) as type int in argument to append - 若删的是最后一个元素(
i == len(nums)-1),nums[i+1:]是空切片[]int{},append仍能正确处理 - 若删的是第一个元素(
i == 0),nums[:i]是空切片,也合法
直接修改底层数组可能引发内存泄漏
上面的方法在多数场景下没问题,但要注意:它**不一定会释放被删元素占用的内存**。因为新切片仍可能指向原底层数组,只要容量足够,GC 就无法回收整块底层数组。
例如:
large := make([]byte, 1000000) small := large[100:105] // 取 5 字节,但底层数组仍是 1e6 字节 // 此时 small 仍持有 large 的全部底层数组引用
如果后续只保留 small 并丢弃 large,那 100 万字节仍无法被 GC 回收。
- 解决方法是强制创建独立底层数组:
small = append([]byte(nil), small...) - 对切片删除操作也同理:若被删切片原本来自大数组,且你担心内存滞留,应在
append后再做一次拷贝:nums = append([]int(nil), nums...) - 这种额外拷贝有性能成本,仅在明确存在大底层数组 + 长期持有小切片的场景才需要
按值删除多个匹配元素要用循环 + 索引偏移
上面的方法只适合删单个已知索引的元素。如果要删所有等于某值的元素(比如删掉所有 0),不能边遍历边删——因为每次删除都会导致后续元素索引前移,容易漏删或越界。
推荐用“双指针”覆盖法,一次遍历完成:
nums := []int{0, 1, 0, 2, 0, 3}
write := 0
for read := 0; read
- 避免了多次
append和内存分配,性能更好 - 不会因索引变动出错,逻辑清晰稳定
- 如果原切片后续不再使用,可直接截断;若需保留原变量名,就赋值回
nums
注意 nil 切片和空切片的边界行为
对 nil 切片(var s []int)或长度为 0 的空切片(s := []int{})执行删除操作时,某些写法会 panic:
-
s = append(s[:i], s[i+1:]...)在i >= len(s)或s为nil时 panic:panic: runtime error: slice bounds out of range - 安全做法是先检查:
if i >= 0 && i -
nil切片的len和cap都是 0,但s[:0]合法,s[0:]也合法;唯独s[0]或s[:1]会 panic
实际项目中,建议封装成带校验的工具函数,而不是裸写 append 行。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











