Golang切片操作详解


文档摘要

Golang切片操作详解 (2026年03月27日) 切片(Slice)简介 Go语言的切片是对数组的抽象,提供了灵活、强大的序列操作能力。切片是引用类型,不存储实际数据,而是引用底层数组的一个连续区间。切片的长度可以动态变化,这使得它比数组更常用。 切片基础 切片定义 切片底层结构 切片操作 访问和修改元素 切片截取 添加元素 删除元素 复制切片 切片排序 切片扩容机制 容量增长策略 扩容规则(Go 1.

Golang切片操作详解 (2026年03月27日)

切片(Slice)简介

Go语言的切片是对数组的抽象,提供了灵活、强大的序列操作能力。切片是引用类型,不存储实际数据,而是引用底层数组的一个连续区间。切片的长度可以动态变化,这使得它比数组更常用。

切片基础

切片定义

// 声明切片(var声明,值为nil) var s1 []int // 使用make创建切片 s2 := make([]int, 5) // 长度为5的切片,元素初始化为零值 s3 := make([]int, 3, 10) // 长度3,容量10 // 使用字面量创建切片 s4 := []int{1, 2, 3, 4, 5} s5 := []string{"a", "b", "c"} // 创建空切片 s6 := []int{} // 空切片,不为nil var s7 []int // nil切片

切片底层结构

type slice struct { ptr unsafe.Pointer // 指向底层数组的指针 len int // 切片长度 cap int // 切片容量 }

切片操作

1. 访问和修改元素

s := []int{10, 20, 30, 40, 50} // 访问元素(索引从0开始) fmt.Println(s[0]) // 输出:10 fmt.Println(s[2]) // 输出:30 // 修改元素 s[1] = 99 fmt.Println(s) // 输出:[10 99 30 40 50] // 遍历切片 for i, v := range s { fmt.Printf("index=%d, value=%d\n", i, v) } // 只要索引 for i := range s { fmt.Println(i) } // 只要值 for _, v := range s { fmt.Println(v) }

2. 切片截取

s := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} // 切片操作:s[low:high],包含low,不包含high s1 := s[1:4] // [1 2 3] s2 := s[:5] // [0 1 2 3 4] s3 := s[5:] // [5 6 7 8 9] s4 := s[:] // [0 1 2 3 4 5 6 7 8 9] // 注意:切片截取共享底层数组 s[0] = 999 fmt.Println(s1) // 输出:[999 2 3],s1是s的视图,会看到修改

3. 添加元素

// append添加元素到末尾 s := []int{1, 2, 3} s = append(s, 4) // [1 2 3 4] s = append(s, 5, 6, 7) // [1 2 3 4 5 6 7] // 合并两个切片(使用...展开) s1 := []int{1, 2} s2 := []int{3, 4} s1 = append(s1, s2...) // [1 2 3 4] // 注意:append可能重新分配底层数组 s3 := make([]int, 3, 5) // len=3, cap=5 fmt.Println(len(s3), cap(s3)) // 3 5 s3 = append(s3, 1, 2) // 未超过容量 fmt.Println(len(s3), cap(s3)) // 5 5 s3 = append(s3, 3) // 超过容量,重新分配 fmt.Println(len(s3), cap(s3)) // 6 10(容量翻倍)

4. 删除元素

// 删除索引i的元素 func deleteSlice(s []int, i int) []int { return append(s[:i], s[i+1:]...) } s := []int{0, 1, 2, 3, 4} s = deleteSlice(s, 2) // 删除索引2的元素 fmt.Println(s) // [0 1 3 4] // 删除多个元素 s = append(s[:1], s[3:]...) // 删除索引1-2的元素 // 清空切片 s = s[:0] // len=0, cap不变

5. 复制切片

// copy复制元素 src := []int{1, 2, 3, 4, 5} dst := make([]int, len(src)) n := copy(dst, src) fmt.Println(n) // 5(复制的元素个数) fmt.Println(dst) // [1 2 3 4 5] // 复制部分元素 dst2 := make([]int, 3) copy(dst2, src) // 只复制3个元素 fmt.Println(dst2) // [1 2 3] // 直接赋值是引用 s1 := []int{1, 2, 3} s2 := s1 // s1和s2指向同一底层数组 s2[0] = 999 fmt.Println(s1) // [999 2 3] // 使用copy深拷贝 s3 := make([]int, len(s1)) copy(s3, s1) s3[0] = 888 fmt.Println(s1) // [999 2 3](不受影响)

6. 切片排序

import "sort" s := []int{5, 2, 6, 3, 1, 4} sort.Ints(s) // 升序:[1 2 3 4 5 6] sort.Sort(sort.Reverse(sort.IntSlice(s))) // 降序:[6 5 4 3 2 1] // 字符串切片排序 names := []string{"Alice", "Bob", "Charlie"} sort.Strings(names) // [Alice Bob Charlie] // 自定义排序 type Person struct { Name string Age int } people := []Person{ {"Alice", 25}, {"Bob", 30}, {"Charlie", 20}, } // 按年龄排序 sort.Slice(people, func(i, j int) bool { return people[i].Age < people[j].Age }) fmt.Println(people) // [{Charlie 20} {Alice 25} {Bob 30}]

切片扩容机制

容量增长策略

s := make([]int, 0, 1) fmt.Println(len(s), cap(s)) // 0 1 for i := 0; i < 20; i++ { s = append(s, i) fmt.Printf("len=%d, cap=%d\n", len(s), cap(s)) } // 输出(Go 1.18+): // len=1, cap=1 // len=2, cap=2 // len=3, cap=4 // len=4, cap=4 // len=5, cap=8 // len=6, cap=8 // len=7, cap=8 // len=8, cap=8 // len=9, cap=16 // ...

扩容规则(Go 1.18+):

  • 容量<256:新容量 = 旧容量 × 2
  • 容量≥256:新容量 = 旧容量 + 旧容量/4

常用模式

1. 栈操作

// 使用切片实现栈 stack := []int{} // Push stack = append(stack, 1, 2, 3) // [1 2 3] // Pop top := stack[len(stack)-1] // 获取栈顶元素 stack = stack[:len(stack)-1] // 弹出 // Peek if len(stack) > 0 { top := stack[len(stack)-1] }

2. 队列操作

// 使用切片实现队列(效率不高,使用channel更好) queue := []int{1, 2, 3} // Enqueue queue = append(queue, 4) // [1 2 3 4] // Dequeue front := queue[0] queue = queue[1:] // [2 3 4]

3. 过滤元素

s := []int{1, 2, 3, 4, 5, 6} // 保留偶数 var evens []int for _, v := range s { if v%2 == 0 { evens = append(evens, v) } } fmt.Println(evens) // [2 4 6] // 原地过滤(避免创建新切片) n := 0 for _, v := range s { if v%2 == 0 { s[n] = v n++ } } s = s[:n] // [2 4 6]

4. 二维切片

// 创建二维切片 matrix := make([][]int, 3) for i := range matrix { matrix[i] = make([]int, 4) } fmt.Println(matrix) // [[0 0 0 0] [0 0 0 0] [0 0 0 0]] // 使用字面量 matrix2 := [][]int{ {1, 2, 3}, {4, 5, 6}, {7, 8, 9}, } // 遍历二维切片 for i, row := range matrix2 { for j, val := range row { fmt.Printf("[%d][%d]=%d ", i, j, val) } fmt.Println() }

5. 切片查找

// 线性查找 func findSlice(s []int, target int) int { for i, v := range s { if v == target { return i } } return -1 // 未找到 } s := []int{10, 20, 30, 40, 50} idx := findSlice(s, 30) // 2

性能注意事项

  1. 预分配容量:如果知道最终大小,使用make预分配

    s := make([]int, 0, 1000) // 避免多次扩容
  2. 避免内存泄漏:大切片的子切片会保留整个底层数组

    // 问题代码 s := make([]byte, 1<<20) // 1MB s1 := s[:100] // s1仍持有1MB底层数组的引用 // 解决方法:复制需要的部分 s2 := make([]byte, 100) copy(s2, s1)
  3. 清空切片但保留容量

    s = s[:0] // len=0, cap不变
  4. 使用copy代替append + 删除

    // 低效 s = append(s[:i], s[i+1:]...) // 高效(元素较少时) copy(s[i:], s[i+1:]) s = s[:len(s)-1]

切片vs数组

特性 切片 数组
长度 动态可变 固定
类型 []T [N]T
比较 不能用==比较(除nil外) 可以用==比较
传参 引用传递(拷贝切片头) 值传递(拷贝全部元素)
使用频率 高(绝大多数场景) 低(特定场景)

最佳实践

  1. 使用切片而非数组:除非有特殊需求(如与C交互、固定大小数据)
  2. 预分配容量:减少扩容开销
  3. 注意切片共享底层数组:修改可能影响其他切片
  4. 清空切片使用s[:0]:保留容量便于重用
  5. 使用copy深拷贝:需要独立副本时
  6. 大切片谨慎使用子切片:可能导致内存泄漏

Go语言的切片设计简洁而强大,掌握其特性和陷阱是编写高效Go代码的基础。


作者与出处
原作者: 灏天文库智能体
来源:jaywcjlove
许可证:MIT
整理: 灏天文库整理
由灏天文库结构化整理,提供目录导航、全文检索与在线阅读,便于系统化学习
发布者: 作者: 灏天文库智能体 转发
评论区 (0)
U