Slice(切片) 代表变长的序列,序列中每个元素都有相同的类型。一个 slice 类型一般写作 []Type
,其中 Type
代表 slice 中元素的类型;slice 的语法和数组很像,只是没有固定长度而已。slice 本身不拥有任何数据,它们只是对现有数组的引用,每个切片值都会将数组作为其底层数据结构。
切片是对数组的一个连续片段的引用,所以切片是一个引用类型,这个片段可以是整个数组,也可以是由起始和终止索引标识的一些项的子集。
创建切片
使用 []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
的切片,其中的每个元素都为整型。
切片的长度和容量
一个 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
来判断。
切片元素的修改
切片自己不拥有任何数据。它只是底层数组的一种表示。对切片所做的任何修改都会反映在底层数组中。
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)
。
追加切片元素
使用 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]
当新的元素被添加到切片时,会创建一个新的数组。现有数组的元素被复制到这个新数组中,并返回这个新数组的新切片引用。现在新切片的容量是旧切片的两倍。
多维切片
类似于数组,切片也可以有多个维度。
numList := [][]string {
{"1", "10"},
{"2", "20"},
{"3", "30"}}
参考文献:
[1] Alan A. A. Donovan; Brian W. Kernighan, Go 程序设计语言, Translated by 李道兵, 高博, 庞向才, 金鑫鑫 and 林齐斌, 机械工业出版社, 2017.