都属于集合类的类型,它们的值用来存储某一类型的值。数组
数组类型的长度是固定的,它必须在声明它的时候就必须给定,而且以后不会再改变。能够说数组的长度是数组类型的一部分,例如[1]string和[2]string就是两个不一样的数组类型。bash
切片类型的长度是可变长的,它的切片类型字面量中只有元素的类型而没有元素的长度。切片的长度能够自动随着其中元素数量的增加而增加,但不会随着元素数量的减小而减小。数据结构
数组和切片都有长度和容量的概念,分别能够经过内建函数len()
和cap()
获取,其中,数组的容量永远 永远等于长度,都是不可变的。而切片的容量虽然相对复杂些但也是有规律可寻,后文后讲解如何估算一个切片的长度和容量。app
本质上来讲,咱们能够把切片看作是对数组的一层简单的封装,每一个切片的底层数据结构都是数组,它能够看做是对数组某个连续片断的引用,这里须要注意的几点是:函数
咱们能够经过切片字面量表达式[]int{1,2,3}
和内建make函数make([]int,5,6)
初始化一个切片,也能够经过切片表达式基于某个数组或切片生成新切片,接下来分别描述下这几种场景:ui
func main() {
s := []int{1, 2, 3, 4, 5, 6}
fmt.Printf("slice length: %d\n", len(s))
fmt.Printf("slice cap: %d\n", cap(s))
}
// 输出
// slice length: 6
// slice cap: 6
复制代码
经过这种方式初始化的切片其长度和容量都等于初始化时传入的元素数量。spa
func main() {
s1 := make([]int, 5)
s2 := make([]int, 5, 6)
fmt.Printf("s1 length: %d\n", len(s1))
fmt.Printf("s1 cap: %d\n", cap(s1))
fmt.Printf("s2 length: %d\n", len(s2))
fmt.Printf("s2 cap: %d\n", cap(s2))
}
// 输出
// s1 length: 5
// s1 cap: 5
// s1 length: 5
// s1 cap: 6
复制代码
内建函数make接收三个参数,第一个参数为切片的类型字面量,第二个变量为切片的长度,第三个变量为切片的容量。当不指明切片容量的时,切片的容量就会和长度一致。3d
这里能够把切片当作一个窗口,经过这个窗口能够看到底层的数组,窗口被划分红一个一个的小格子,每一个格子表明一个数组元素,但由于窗口大小有限所以不能看到数组中全部的元素,大部分时候只能看到数组连续的一部分元素。code
当咱们经过make函数或切片值字面量初始化的切片,它的第一个元素老是会对应其底层数组的第一个元素,在这种状况下,切片的容量就等于其底层数组的长度。拿s2为例,窗口最左边的格子对应的正好是其底层数组索引为0的第一个元素,所以,s2中索引从0到4的元素为其底层数组中索引从0到4表明的那5个元素。cdn
func main() {
s3 := []int{1, 2, 3, 4, 5, 6, 7, 8}
s4 := s3[3:6]
fmt.Printf("The length of s4:%d\n", len(s4))
fmt.Printf("The capacity of s4:%d\n", cap(s4))
fmt.Printf("The value of s4:%d\n", s4)
}
// 输出
// The length of s4:3
// The capacity of s4:5
// The value of s4:[4 5 6]
复制代码
在这里须要明白s3[3:6]
切片表达式中方括号两个整数其实就是数学中的区间表示法,其中3为起始索引,6为终止索引,表明s4从s3中索引为3的元素开始到索引为5的元素结束(不包含6),s4的长度就是6-3=3。所以咱们能够说s4中的索引从0到2指向的元素对应的是s3中索引从3到5的那3个元素。
再来看看容量,一个切片的容量能够看作为经过这个窗口最多能够看到的底层数组重元素的个数。因为s4是在s3的基础上经过切片操做得来的,因此s4的底层数组就是s3的底层数组,所以s4能够向右扩展直至数组末尾,它的容量就是其底层数组的长度8减去s3的起始索引3,即5。
注意这里切片是没法向左扩展的,所以是永远没法透过s4的窗口看到s3的前三个元素的。
若要将s4的窗口向右扩展到最大,能够经过切片表达式s4[0,cap(s4)]作到,它的结果值为[]int{4,5,6,7,8}
func main() {
s5 := []int{1, 2, 3, 4, 5, 6}
s6 := s5[0:2]
s6 = append(s6, 66)
fmt.Printf("The value of s6:%d\n", s6)
fmt.Printf("The value of s5:%d\n", s5)
}
// 输出
// The value of s6:[1 2 66]
// The value of s5:[1 2 66 4 5 6]
复制代码
能够经过append函数对切片进行扩展(这里append函数返回的是一个新切片,所以须要用切片类型的变量去接收它的返回值),只要新长度不会超过切片的原容量时,那么使用append函数对其追加元素时就不会引发扩容,只会使得紧邻切片窗口右边的(底层数组中的)元素被新的元素替换掉,生成一个指向原先底层数组的新切片。
当新长度超过切片的容量时,append函数返回的是指向新底层数组的新切片,它会生成一个新的底层数组,而后将原有的元素和新的元素拷贝到新数组中,而后再生成一个新切片指向这个新的底层数组,在通常状况下,能够简单地认为新切片的容量时原切片容量的两倍。