Go 切片的 append 操作总结

预备

  • 切片是对其底层数组的某一段的引用。
  • 切片有 lencap 两个属性,代表切片的引用长度和切片的容量(从切片的引用起点位置到其底层数组最末端的长度。因为其底层数组的长度是固定的,这也就是意味着容量是指切片可引用的最大宽度)。

append 函数

slice2 := append(slice1, 23, 15)

以上对切片 slice1 进行 append 操作。该操作遵循以下原则:

  1. append 函数对一个切片 slice1 进行追加操作,并返回另一个长度为 len(slice1) + 追加个数 的切片,原切片不被改动,两个切片所指向的底层数组可能是同一个也可能不是,取决于第二条:
  2. 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 操作的切片变量的类型和返回的切片的类型相同,所以一般将返回值再赋予给原变量。这样被操作的切片变量在操作之后:

  1. 变为对原底层数组新的一段长度的引用,或:
  2. 变为对新数组的引用,原底层数组若无其他地方引用,内存将会被回收。
我来评几句
登录后评论

已发表评论数()

相关站点

热门文章