0
点赞
收藏
分享

微信扫一扫

Go 语言系列11:切片

慕犹清 2022-11-25 阅读 114

Slice(切片) 代表变长的序列,序列中每个元素都有相同的类型。一个 slice 类型一般写作 ​​[]Type​​​ ,其中 ​​Type​​ 代表 slice 中元素的类型;slice 的语法和数组很像,只是没有固定长度而已。slice 本身不拥有任何数据,它们只是对现有数组的引用,每个切片值都会将数组作为其底层数据结构。

切片是对数组的一个连续片段的引用,所以切片是一个引用类型,这个片段可以是整个数组,也可以是由起始和终止索引标识的一些项的子集。


Go 语言系列11:切片_引用类型

创建切片

Go 语言系列11:切片_数组_02


使用 ​​[]Type​​​ 可以创建一个带有 ​​Type​​ 类型元素的切片。

// 声明整型切片
var numList []int

// 声明一个空切片
var numListEmpty = []int{}

当然,我们可以通过对数组进行片段截取创建一个切片。

arr := [5]int{10, 20, 30, 40, 50}
var numList = arr[1:4]
fmt.Println(arr) // [10 20 30 40 50]
fmt.Println(numList) // [20 30 40]

注意,使用语法 ​​arr[start:end]​​​ 是创建一个从 ​​arr​​​ 数组索引 ​​start​​​ 开始到 ​​end - 1​​​ 结束的切片,区间左闭右开。因此,在上面的例子中, ​​arr[1:4]​​​ 从索引 ​​1​​​ 到 ​​3​​​ 创建了 ​​arr​​​ 数组的一个切片。因此,切片 ​​numList​​​ 的值为 ​​[20 30 40]​​ 。

你也可以使用 ​​make​​​ 函数构造一个切片,格式为 ​​make([]Type, size, cap)​​ 。

numList := make([]int, 3, 5)

上面的例子创建了一个长度为 ​​3​​​ ,容量为 ​​5​​ 的切片,其中的每个元素都为整型。

Go 语言系列11:切片_引用类型

切片的长度和容量

Go 语言系列11:切片_数组_02


一个 slice 由三个部分构成:指针长度容量 。指针指向第一个 slice 元素对应的底层数组元素的地址,要注意的是 slice 的第一个元素并不一定就是数组的第一个元素。长度对应 slice 中元素的数目;长度不能超过容量,容量一般是从 slice 的开始位置到底层数据的结尾位置。简单的讲,容量就是从创建切片索引开始的底层数组中的元素个数,而长度是切片中的元素个数。

内置的 ​​len​​​ 和 ​​cap​​ 函数分别返回 slice 的长度和容量。

numList := make([]int, 3, 5)
fmt.Println(len(numList)) // 3
fmt.Println(cap(numList)) // 5

如果切片操作超出 ​​cap(numList)​​​ 的上限将导致一个 ​​panic​​​ 异常,但是超出 ​​len(numList)​​ 则是意味着扩展了 slice ,因为新 slice 的长度会变大。

numList := make([]int, 3, 5)
numListEx := numList[:5]
fmt.Println(numList) // [0 0 0]
fmt.Println(numListEx) // [0 0 0 0 0]

由于 slice 是引用类型,所以你不对它进行赋值的话,它的默认值是 ​​nil​​ 。

var numList []int
fmt.Println(numList == nil) // true

切片之间不能比较,因此我们不能使用 ​​==​​​ 操作符来判断两个 slice 是否含有全部相等元素。不过标准库提供了高度优化的 ​​bytes.Equal​​​ 函数来判断两个字节型 slice 是否相等(​​[]byte​​​),但是对于其他类型的 slice ,我们必须自己展开每个元素进行比较。特别注意,如果你需要测试一个 slice 是否是空的,使用 ​​len(s) == 0​​​ 来判断,而不应该用 ​​s == nil​​ 来判断。


Go 语言系列11:切片_引用类型

切片元素的修改

Go 语言系列11:切片_数组_02


切片自己不拥有任何数据。它只是底层数组的一种表示。对切片所做的任何修改都会反映在底层数组中。

var arr = [...]int{10, 20, 30}
numList := arr[:]
fmt.Println(arr) // [10 20 30]
fmt.Println(numList) // [10 20 30]
numList[0] = 55
fmt.Println(arr) // [55 20 30]
fmt.Println(numList) // [55 20 30]

这里的 ​​arr[:]​​​ 没有填入起始值和结束值,默认就是 ​​0​​​ 和 ​​len(arr)​​ 。


Go 语言系列11:切片_引用类型

追加切片元素

Go 语言系列11:切片_数组_02


使用 ​​append​​​ 可以将新元素追加到切片上。​​append​​​ 函数的定义是 ​​func append(slice []Type, elems ...Type) []Type​​​ 。其中 ​​elems ...Type​​​ 在函数定义中表示该函数接受参数 ​​elems​​ 的个数是可变的。这些类型的函数被称为可变函数。

numList := []int{1, 2}
// 追加一个元素 3
numList = append(numList, 3)
// 追加两个元素 4 5
numList = append(numList, 4, 5)
// 追加一个切片 ... 表示解包不能省略
numList = append(numList, []int{6, 7}...)
// 在第一个位置插入一个元素 0
numList = append([]int{0}, numList...)
fmt.Println(numList) // [0 1 2 3 4 5 6 7]

当新的元素被添加到切片时,会创建一个新的数组。现有数组的元素被复制到这个新数组中,并返回这个新数组的新切片引用。现在新切片的容量是旧切片的两倍。


Go 语言系列11:切片_引用类型

多维切片

Go 语言系列11:切片_数组_02


类似于数组,切片也可以有多个维度。

numList := [][]string {
{"1", "10"},
{"2", "20"},
{"3", "30"}}

参考文献:

[1] Alan A. A. Donovan; Brian W. Kernighan, Go 程序设计语言, Translated by 李道兵, 高博, 庞向才, 金鑫鑫 and 林齐斌, 机械工业出版社, 2017.




举报

相关推荐

0 条评论