预备
- 切片是对其底层数组的某一段的引用。
- 切片有
len
和cap
两个属性,代表切片的引用长度和切片的容量(从切片的引用起点位置到其底层数组最末端的长度。因为其底层数组的长度是固定的,这也就是意味着容量是指切片可引用的最大宽度)。
append 函数
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
操作的切片变量的类型和返回的切片的类型相同,所以一般将返回值再赋予给原变量。这样被操作的切片变量在操作之后:
- 变为对原底层数组新的一段长度的引用,或:
- 变为对新数组的引用,原底层数组若无其他地方引用,内存将会被回收。
有疑问加站长微信联系(非本文作者)