Go 语言中的切片(Slice)是一个非常核心的数据结构,它是对数组的抽象和封装,提供了更灵活、强大的序列处理能力。
一. 切片的基本概念
切片是一个动态数组,它由三个部分组成:
-
指针:指向底层数组的起始位置
-
长度(len):切片中当前元素的数量
-
容量(cap):切片可以容纳的最大元素数量
注意:切片 不是数组本身,而是数组的一层抽象。
二、数组 vs 切片(先分清)
数组(Array)
Go
var a [3]int = [3]int{1, 2, 3}
-
长度固定
-
值类型
-
很少直接用在业务代码中
切片(Slice)
Go
var s []int = []int{1, 2, 3}
-
长度可变
-
引用语义
-
Go 业务代码的主角
三、切片的底层结构(非常重要)
Go 的切片在运行时是一个结构体:
Go
type slice struct {
ptr *T // 指向底层数组
len int // 当前长度
cap int // 容量
}
知识的三点:
-
len:当前"能用"的元素个数 -
cap:从ptr开始,底层数组最多能放多少 -
多个切片 可能共享同一个底层数组
四、创建切片的几种方式
1. 字面量创建(最常用)
Go
s := []int{1, 2, 3}
-
len = 3
-
cap = 3
2. 使用make函数创建(最推荐)
Go
s := make([]int, 2, 5)
含义:
-
长度 = 2(已有 2 个元素,默认值)
-
容量 = 5(最多可扩到 5),可缺省。
3. 从数组 / 切片切出来
Go
arr := [5]int{1, 2, 3, 4, 5}
s := arr[1:4] // [2 3 4]
规则:
Go
s := a[low : high] // 左闭右开
这是"视图",不是拷贝
五、append:切片的扩容机制
基本用法,append会触发cap翻倍扩容
Go
s := []int{1, 2}
s = append(s, 3)
//cap扩容为原来的2倍
fmt.Println(len(s), cap(s))
输出:3,4
扩容规则
-
小容量:2 倍增长
-
大容量:逐渐放缓(≈1.25x)
-
具体策略由 Go runtime 决定
六、如何安全拷贝切片
使用 copy(官方推荐)
Go
src := []int{1, 2, 3}
dst := make([]int, len(src))
copy(dst, src)
fmt.Println(dst)
输出:[1 2 3]
七、nil slice vs 空 slice
Go
var s1 []int // nil slice
s2 := []int{} // 空 slice
s3 := make([]int,0) // 空 slice
| 属性 | s1 | s2 / s3 |
|---|---|---|
| len | 0 | 0 |
| cap | 0 | 0 |
| == nil | true | false |
| append | ✔ | ✔ |
八、切片作为函数参数
Go
func modify(s []int) {
s[0] = 100
}
-
修改元素 → 会影响外部
-
append 扩容 → 不一定影响外部
因为 slice 本身是 值传递(复制 slice 结构体)
九、遍历切片
for
Go
for i := 0; i < len(s); i++ {
fmt.Println(s[i])
}
range(最常用)
Go
for i, v := range s {
fmt.Println(i, v)
}