1. 删除指定索引的元素 — append 切片拼接
Go 没有内置的"删除元素"操作,用 append 把前半段和后半段拼接起来:
package main
import "fmt"
func main() {
data := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
// 删除索引3的元素(值=4)
// append(data[:3], data[4:]...) → [1,2,3] + [5,6,7,8,9,10]
index := 3
data = append(data[:index], data[index+1:]...)
fmt.Println("After removing index 3:", data)
}
执行结果:
After removing index 3: [1 2 3 5 6 7 8 9 10]
要点:
-
data[:index]— 索引之前的元素:[1, 2, 3] -
data[index+1:]— 索引之后的元素:[5, 6, 7, 8, 9, 10] -
append(data[:index], data[index+1:]...)— 把两段拼接,中间的元素 4 被删除 - 这是 Go 删除切片元素的标准模式:append 前半段 + 后半段
- 注意:这个操作修改了底层数组!删除后 data 的长度变短,但底层数组末尾仍有一个残留元素(原 data[9]=10 被复制到新位置,旧位置仍有值,只是不可访问)
- 如果元素是指针类型或包含指针,需先置 nil 防止内存泄漏:
data[index] = nil // 先置空,再 append data = append(data[:index], data[index+1:]...)
2. 在指定索引插入元素 — 双层 append
插入元素也用 append,分三步:取前半段 → append 新值 → append 后半段:
package main
import "fmt"
func main() {
data := []int{1, 2, 3, 5, 6, 7, 8, 9, 10} // 删除4之后的切片
// 在索引2处插入值99
insertIndex := 2
insertValue := 99
// 步骤拆解:
// 1. append(data[:2], 99) → [1, 2, 99]
// 2. append([1, 2, 99], data[2:]...) → [1, 2, 99, 3, 5, 6, 7, 8, 9, 10]
data = append(append(data[:insertIndex], insertValue), data[insertIndex:]...)
fmt.Println("After inserting 99 at index 2:", data)
}
执行结果:
After inserting 99 at index 2: [1 2 99 99 5 6 7 8 9 10]
要点:
-
append(data[:insertIndex], insertValue)— 在前半段末尾追加新值,得到 [1, 2, 99] -
append([1,2,99], data[insertIndex:]...)— 拼接后半段,得到 [1, 2, 99, 3, 5, …] - 双层 append 嵌套,内层 append 返回的切片作为外层 append 的第一个参数
- 注意输出中有两个 99!这是因为内层
append(data[:2], 99)在原底层数组上操作:-
data[:2]= [1, 2],append 99 后底层位置 data[2] 被写为 99 - 外层
append([1,2,99], data[2:]...)中data[2:]从原底层数组读取,data[2] 已经是 99 - 所以出现了两个 99 — 这是 append 在共享底层数组上操作的副作用
-
- 更安全的做法:先复制前半段到新切片,避免修改原底层数组:
// 安全版本:使用临时切片 temp := make([]int, insertIndex) copy(temp, data[:insertIndex]) temp = append(temp, insertValue) data = append(temp, data[insertIndex:]...)
3. 原地反转切片 — 双指针交换
用两个指针从两端向中间交换元素,时间复杂度 O(n),空间复杂度 O(1):
package main
import "fmt"
func main() {
numbers := []int{1, 2, 3, 4, 5}
// 双指针:i 从头开始,j 从尾开始,交换后 i++ j--
for i, j := 0, len(numbers)-1; i < j; i, j = i+1, j-1 {
numbers[i], numbers[j] = numbers[j], numbers[i]
}
fmt.Println("Reversed numbers:", numbers)
}
执行结果:
Reversed numbers: [5 4 3 2 1]
要点:
-
for i, j := 0, len(numbers)-1; i < j; i, j = i+1, j-1— 初始化两个指针,i++ j-- 同时更新 -
numbers[i], numbers[j] = numbers[j], numbers[i]— Go 的多重赋值,同时交换两个元素 -
i < j— 当 i 和 j 相遇或越过时停止(奇数长度时中间元素不交换) - 原地操作:不需要额外空间,直接在原切片上交换
- 交换过程:1↔5 → 2↔4 → 3不动(中间)→ 结果 [5, 4, 3, 2, 1]
- Go 没有内置的反转函数,这是反转切片的标准写法(Go 官方 Wiki 推荐)
4. 过滤删除所有指定值 — append 收集保留值
遍历切片,把不等于目标值的元素收集到新切片:
package main
import "fmt"
func main() {
values := []int{1, 2, 3, 2, 4, 2, 5}
target := 2
// 创建新切片,收集非目标值
var filtered []int
for _, v := range values {
if v != target {
filtered = append(filtered, v)
}
}
fmt.Printf("After removing all %d: %v\n", target, filtered)
}
执行结果:
After removing all 2: [1 3 4 5]
要点:
-
var filtered []int— nil 切片,append 自动分配底层数组 - 遍历原切片,
v != target时 append 到新切片 - 结果
[1, 3, 4, 5]— 所有 2 被移除 - 这是"过滤"模式:遍历 + 条件判断 + append,Go 中极常用
- 更高效的就地版本(不分配新底层数组):
n := 0 for _, v := range values { if v != target { values[n] = v n++ } } values = values[:n] // 截断到新长度
5. 检查切片是否包含某值 — 手动遍历
Go 1.18 之前没有内置的 contains 函数,需要手动遍历(Go 1.21+ 有 slices.Contains):
package main
import "fmt"
func main() {
haystack := []string{"apple", "banana", "cherry", "date"}
needle := "cherry"
var found bool
for _, item := range haystack {
if item == needle {
found = true
break // 找到后立即退出
}
}
if found {
fmt.Printf("Found '%s' in slice\n", needle)
} else {
fmt.Printf("'%s' not found in slice\n", needle)
}
}
执行结果:
Found 'cherry' in slice
要点:
遍历切片逐个比较,
break找到后立即退出var found bool— 默认值 false,找到后设为 true-
Go 1.21+ 可以用
slices.Contains(haystack, needle)更简洁:import "golang.org/x/exp/slices" // 或 Go 1.21: "slices" found := slices.Contains(haystack, needle) 对于排序切片,可以用二分搜索提高效率:
sort.SearchStrings(slice, target)
6. 去重 — map 记录已见元素
用 map 的键来跟踪哪些元素已经出现过,实现去重:
package main
import "fmt"
func main() {
duplicates := []int{1, 2, 2, 3, 1, 4, 3, 5}
// map 记录已见元素,只保留首次出现的
seen := make(map[int]bool)
var unique []int
for _, v := range duplicates {
if !seen[v] { // v 还没出现过
seen[v] = true // 标记为已见
unique = append(unique, v) // 加入结果
}
}
fmt.Println("Unique elements:", unique)
}
执行结果:
Unique elements: [1 2 3 4 5]
要点:
-
seen := make(map[int]bool)— map 的键是元素值,bool 标记是否已见 -
!seen[v]— v 不在 map 中 = 首次出现,加入结果并标记 -
seen[v] = true— 标记已见,下次遇到相同值时跳过 - 结果
[1, 2, 3, 4, 5]— 保持了首次出现的顺序 - 去重效率:O(n) 时间(map 查找是 O(1)),O(n) 空间(map 和结果切片)
- 如果不关心顺序,直接用 map 的键即可:
seen := make(map[int]struct{}) // struct{} 不占内存 for _, v := range duplicates { seen[v] = struct{}{} } unique := make([]int, 0, len(seen)) for k := range seen { unique = append(unique, k) } // 注意:map 遍历顺序不确定
知识点总结
| 知识点 | 关键概念 |
|---|---|
| 删除元素 |
append(data[:i], data[i+1:]...) — 前半段 + 后半段拼接 |
| 插入元素 |
append(append(data[:i], val), data[i:]...) — 双层 append,注意底层数组副作用 |
| 反转切片 | 双指针交换 for i,j := 0,len-1; i<j,Go 官方推荐写法 |
| 过滤删除 | 遍历 + 条件判断 + append 收集保留值 |
| 包含检查 | 手动遍历 + break,Go 1.21+ 用 slices.Contains
|
| 去重 |
map[元素]bool 记录已见,保持首次出现顺序 |