Go 切片的 append 操作总结
预备
切片是对其底层数组的某一段的引用。
切片有 len 和 cap 两个属性,代表切片的引用长度和切片的容量(从切片的引用起点位置到其底层数组最末端的长度。因为其底层数组的长度是固定的,这也就是意味着容量是指切片可引用的最大宽度)。
append 函数,指针指向底层数组中的切片起始位置,数据直接访问(slice[index])时, index 值不能超过 len(slice) 范围,创建切片的切片(slice[start:end])时, start 和 end 指定的区间不能超过 cap(slice) 范围。
slice2 := append(slice1, 23, 15)
以上对切片 slice1 进行 append 操作。该操作遵循以下原则:
append 函数对一个切片 slice1 进行追加操作,并返回另一个长度为 len(slice1) + 追加个数 的切片,原切片不被改动,两个切片所指向的底层数组可能是同一个也可能不是,取决于第二条:
slice1 是对其底层数组的一段引用,若 append 追加完之后没有突破 slice1 的容量,则实际上追加的数据改变了其底层数组对应的值,并且 append 函数返回对底层数组新的引用(切片);若 append 追加的数据量突破了 slice1 的最大容量(底层数组长度固定,无法增加长度赋予新值),则 Go 会在内存中申请新的数组(数组内的值为追加操作之后的值),并返回对新数组的引用(切片)。
示例
// 声明并初始化长度为 5 的整型数组 [0 0 0 0 0]
var arr [5]int
// slice1 和 slice2 是对 arr 第 2 个元素到第 4 个元素的引用
slice1 := arr[1:4] // slice1: [0 0 0]
slice2 := arr[1:4] // slice2: [0 0 0]
// 对切片的修改会反映到底层数组
slice1[0] = 1 // slice1:[1 0 0] slice2:[1 0 0] arr:[0 1 0 0 0]
// 对底层数组的修改同样会反映到指向它的切片
arr[2] = 2 // slice1:[1 2 0] slice2:[1 2 0] arr:[0 1 2 0 0]
// 因为对 slice1 的追加没有突破其底层数组的长度,所以返回的切片还是指向原来的底层数组
slice3 := append(slice1, 4) // slice1:[1 2 0] slice2:[1 2 0] slice3:[1 2 0 4] arr:[0 1 2 0 4]
slice3[2] = 3 // slice1:[1 2 3] slice2:[1 2 3] slice3:[1 2 3 4] arr:[0 1 2 3 4]
// 如果对切片的追加突破了底层数组的长度,则会分配一个新的数组,返回指向新数组的切片
slice3 = append(slice3, 5) // slice1:[1 2 3] slice2:[1 2 3] slice3:[1 2 3 4 5] arr:[0 1 2 3 4]
// slice3 的底层数组已经改变,对它的操作不会影响到 slice1 slice2 和 arr
slice3[0] = 6 // slice1:[1 2 3] slice2:[1 2 3] slice3:[6 2 3 4 5] arr:[0 1 2 3 4]
惯用方式
slice1 = append(slice1, 5, 6)
因为 append 操作的切片变量的类型和返回的切片的类型相同,所以一般将返回值再赋予给原变量。这样被操作的切片变量在操作之后:
变为对原底层数组新的一段长度的引用,或:
变为对新数组的引用,原底层数组若无其他地方引用,内存将会被回收。
例如:package main
import “fmt”
func main() {
s := make([]int, 0, 5)
s = append(s, []int{1, 2, 3, 4}…)
a := append(s, 5)
fmt.Println(a)
b := append(s, 6)
fmt.Println(b)
fmt.Println(a)
}
如果你运行这个,你会得到:
[1 2 3 4 5]
[1 2 3 4 6]
[1 2 3 4 6]
因为既然s仍然有能力,都a和b共享相同的数据PTR。如果将容量更改为 4,则会打印:
[1 2 3 4 5]
[1 2 3 4 6]
[1 2 3 4 5]
Go切片与append详解
本文深入探讨了Go语言中切片的概念及其append操作的特点。解释了切片是如何引用底层数组的一部分,并讨论了append操作如何根据切片容量的变化决定是否重新分配内存。
691

被折叠的 条评论
为什么被折叠?



