切片(Slice)是一个拥有相同类型元素的可变长度的序列。切片是引用类型,本身并不存储任何数据,其内部包括地址
、长度
、容量
,其底层是数组,所以地址
指向底层数组的对应元素。另外,长度
是切片的元素个数,容量
是长度
的最大值,超过则需要扩容。
切片长度和容量可以通过内置的len()
函数和cap()
函数获得。
var s = []int{1, 2, 3}
fmt.Println("长度:", len(s), "容量:", cap(s)) // 长度: 3 容量: 3
直接声明得到的是空切片,其值等于nil
,nil
是非基础类型的零值,如指针、通道、函数、接口、映射或切片的零值。nil切片
长度和容量为0且没有底层数组。
var s1 []int
fmt.Println(s1) // []
fmt.Println(s1 == nil) // true
通过初始化列表得到切片,切片长度与容量等于初始化列表的元素个数。其本质是生成一个底层数组,然后让声明的切片指向该底层数组。
var s2 = []int{1, 2, 3}
s3 := []string{"我", "爱", "中", "国"}
fmt.Println(s2 == nil, s2, s3) // false [1 2 3] [我 爱 中 国]
fmt.Println(len(s3), cap(s3)) // 4 4
切片通过两个下标来界定,即一个上界和一个下界,二者以冒号分隔:
a[low : high]
它会选择一个半开区间,包括第一个元素,但排除最后一个元素。
切片下界的默认值为 0,上界则是该切片的长度。
var a = [...]int{0, 1, 2, 3, 4, 5}
var s4 = a[0:3]
fmt.Println(s4) // [0 1 2]
var s5 = a[:]
fmt.Println(s5) // [0 1 2 3 4 5]
s5 = s4[1:]
fmt.Println(s5) // [0 1 2]
切片容量是切片第一个元素到底层数组最后一个元素的长度。
var s6 = a[2:4]
fmt.Println(s6, cap(s6)) // [2 3] 4
切片可以再切片,再切片的上界取决于原切片的容量。
var s7 = s6[1:3]
fmt.Println(s7, cap(s7)) // [3 4] 3
上述示例中的切片s4~s7的底层数组都是 a,更改切片的元素会修改其底层数组中对应的元素,与它共享底层数组的切片都会观测到这些修改。
s6[1] = 10
fmt.Println(s7) // [10 4]
切片只能和nil
比较,即s == nil
;如果是s1 == s2
则会报错。
参考1、参考2