「这是我参与2022首次更文挑战的第10天,活动详情查看:2022首次更文挑战」。
复习切片的结构
- ptr:指向底层数组的指针
- len:切片长度
- cap:切片容量,底层数组最大长度
make
- 切片可以用内建函数 make 来创建,这也是创建动态数组的方式
- make 函数会分配一个元素为零值的数组并返回一个指向(引用)了它的切片
语法格式
func make([]T, len, cap) []T
[]T代表创建的切元素的类型- len:切片长度长度
- cap:可选的切片容量,不指定 cap 则默认为 len 的长度
具体的🌰
package main
import "fmt"
func main() {
var s1 = make([]int, 5)
printSlice(s1)
s2 := make([]int, 5, 10)
printSlice(s2)
// reslice
b := make([]int, 0, 5) // len(b)=0, cap(b)=5
printSlice(b)
// 向后扩展
b = b[:cap(b)] // len(b)=5, cap(b)=5
printSlice(b)
// ptr 指向了第二个元素,所以 cap 也变成
b = b[1:] // len(b)=4, cap(b)=4
printSlice(b)
}
func printSlice(s []int) {
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}
运行结果
len=5 cap=5 [0 0 0 0 0]
len=5 cap=10 [0 0 0 0 0]
len=0 cap=5 []
len=5 cap=5 [0 0 0 0 0]
len=4 cap=4 [0 0 0 0]
make 的原理
s := make([]int, 5)
append
将元素追加到切片的末尾,并返回切片
语法格式
func append(slice []Type, elems ...Type) []Type
slice是一个元素类型为Type的切片,其余类型为Type的值将会追加到切片的末尾- 返回值是一个包含原切片所有元素加上新添加元素的切片
- 当
slice的底层数组太小,添加元素时超过 cap,系统会重新分配更大的底层数组,返回的切片会指向这个新分配的数组 - 若切片有足够的容量(cap),会直接在原切片末尾追加元素
返回值
- 返回更新后的切片
- 必须接收 append 后的返回值
- 因为如果系统分配了更大的新底层数组,那么切片的指针需要重新指向新分配的数组
// slice 切片追加元素 elem1, elem2
slice = append(slice, elem1, elem2)
// slice 切片追加另一个切片 anotherSlice
slice = append(slice, anotherSlice...)
具体的🌰
package main
import "fmt"
func main() {
var s []int
printSlice(s)
// 添加一个空切片
s = append(s, 0)
printSlice(s)
// 这个切片会按需增长
s = append(s, 1)
printSlice(s)
// 可以一次性添加多个元素
s = append(s, 2, 3)
printSlice(s)
s = append(s, 2, 3, 4)
printSlice(s)
}
func printSlice(s []int) {
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}
运行结果
len=0 cap=0 []
len=1 cap=1 [0]
len=2 cap=2 [0 1]
len=4 cap=4 [0 1 2 3]
len=7 cap=8 [0 1 2 3 2 3 4]
切片的 cap 不足以添加所有元素时,系统会自动创建一个新的、更大容量的切片,然后将原有切片的内容复制到新的切片,最后再返回
再来看一个🌰
package main
import "fmt"
func main() {
// 定义一个数组
var arr = [5]int{1, 2, 3, 4, 5}
s1 := arr[:3]
s2 := append(s1, 44, 55)
s3 := append(s2, 66)
s4 := append(s3, 77)
fmt.Println(s2, s3, s4)
fmt.Println(arr)
printSlice(s1)
printSlice(s2)
printSlice(s3)
printSlice(s4)
}
func printSlice(s []int) {
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}
运行结果
[1 2 3 44 55] [1 2 3 44 55 66] [1 2 3 44 55 66 77]
[1 2 3 44 55]
len=3 cap=5 [1 2 3]
len=5 cap=5 [1 2 3 44 55]
len=6 cap=10 [1 2 3 44 55 66]
len=7 cap=10 [1 2 3 44 55 66 77]
- 切片 s1 本来就指向数组 arr,s2 指向 s1,所以它们的容量还是 5
- s1、s2 添加元素后切片大小没有大于 cap,所以切片 s1、s2还是指向 arr 数组,而添加的元素也会同步修改数组 arr 对应位置的值
- s3、s4 添加元素后切片大小已经大于 cap,所以会分配更大的底层数组,s3、s4 会重新指向新的数组,所以添加的元素自然不会影响数组 arr
特殊情况
将字符追加到字节数组之后是合法的
slice = append([]byte("hello "), "world"...)
将切片追加到另一个切片尾部
package main
import "fmt"
func main() {
a := []string{"John", "Paul"}
b := []string{"George", "Ringo", "Pete"}
// 等价写法 append(a, b[0], b[1], b[2])
a = append(a, b...) // b后面加了 ...
printSlice(a)
}
func printSlice(s []string) {
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}
运行结果
len=5 cap=5 [John Paul George Ringo Pete]
copy
- 将元素从源切片复制到目标切片
- 作为一种特殊情况,它还会将字节从字符串复制到字节切片
- 源和目标可能会重叠
- 函数返回复制的元素数,会是 len(src) 和 len(dst) 中的最小值
语法格式
func copy(dst, src []Type) int
具体的🌰
package main
import "fmt"
func main() {
arr := [...]int{1, 2, 3, 4, 5}
s1 := arr[:3]
s2 := make([]int, 5)
printSlice(s1)
printSlice(s2)
copy(s2, s1)
printSlice(s2)
}
func printSlice(s []int) {
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}
运行结果
len=3 cap=5 [1 2 3]
len=5 cap=5 [0 0 0 0 0]
len=5 cap=5 [1 2 3 0 0]