Slice(切片)- 《Go 专家编程》笔记提要
切片(slice)
slice 又称动态数组,依托数组实现,可以方便地进行扩容和传递,实际使用时比数组更灵活。
初始化
- 声明变量
- 变量值都为零值,对于切片来讲零值为 nil
- 字面量
- 空切片是指长度为空,而值不是 nil
- 声明长度为0的切片时,推荐使用变量声明的方式获得一个 nil 切片,而不是空切片,因为 nil 切片不需要内存分配
- 内置函数make()
- 推荐指定长度同时指定预估空间,可有效地减少切片扩容时内存分配及拷贝次数
- 切取
- 切片与原数组或切片共享底层空间
// 1. 声明变量
var s []int // nil 切片
// 2. 字面量
s1 := []int{} // 空切片
s2 := []int{1, 2, 3} // 长度为3的切片
// 3. 内置函数 make()
s3 := make([]int, 12) // 指定长度
s4 := make([]int, 12, 100) // 指定长度和空间
// 4. 切取
arr := [5]int{1, 2, 3, 4, 5}
s5 := arr[2:4] // 从数组中切取
s6 ;= s5[1:2] // 从切片中切取
操作
append()
当切片空间不足时,append()
会先创建新的大容量切片,添加元素后再返回新切片。
扩容规则
- 原 slice 的容量小于 1024,则新 slice 的容量将扩大为原来的 2 倍
- 原 slice 的容量大于或等于 1024,则新 slice 的容量将扩大为原来的 1.25 倍
append()
向 slice 添加一个元素的实现步骤如下:
- 加入 slice 的容量够用,则将新元素追加进去,slice.len++,返回 slice
- 原 slice 的容量不够,则将 slice 先扩容,扩容后得到新 slice
- 将新元素追加进新 slice, slice.len++,返回新的 slice
s := make([]int, 0)
s = append(s, 1) // 添加1个元素
s = append(s, 2, 3, 4 ,5) // 添加多个元素
s = append(s, []int{6, 7}...) //添加一个切片
len()
和 cap()
由于切片的本质为结构体,结构体中存储了切片的长度和容量,所以这两个操作的时间复杂度均为 O(1)
copy()
会将源切片的数据逐个拷贝到目的切片指向的数组中,拷贝数量取两个切片长度的最小值。
例如长度为 10 的切片拷贝到长度为 5 的切片中时,将拷贝 5 个元素
也就是说,拷贝过程中不会发生扩容。
实现原理
src/runtime/slice.go:slice
type slice struct {
array unsafe.Pointer
len int
cap int
}
array 指针指向底层数组,len 表示切片长度,cap 表示数组容量
切片表达式
- 简单表达式 s[low : high]
- 扩展表达式 s[low : high : max]
使用简单表达式生成的切片将与原数组或切片共享底层数组。新切片的生成逻辑可以使用一下伪代码表示:
b.array = &a[low]
b.len = high - low
b.cap = len(a) - low
// 扩展表达式中的 max 用于限制新生成切片的容量,新切片的容量为 max - low
b.cap = max - low
如果切片表达式发生越界就触发 panic
切取 string
作用于字符串时会则会产生新的字符串,而不是切片
扩展表达式不能用于切取 string
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: