定义
Go语言中切片是对数组的抽象。
由于在Go语言中,数组的长度不可改变,但是在特定场景中这样的集合就不太适用,所以Go中提供了一种灵活、功能强悍的内置类型——切片(可以理解为"动态数组"),与数组相比,切片的长度是可变的,可以往右边追加元素,在追加时可能使切片的容量增大。
切片的两个重要的概念是长度(len)和容量(cap),下面我们通过一段代码来理解下这两者的区别
slice1 := make([]int, 5)
slice2 := make([]int, 5, 8)
arry1 := []int{1,2,3,4,5}
slice3 := arry1[2:4]
如上述,有3种方式定义切片,第一种长度和容量一直,都是5,第二种长度是5,容量为8,而第三种长度为2,容量为3。
大家都知道,实际使用切片比较多的场景,都是基于数组的,即上述的第三种方式。
既然切片是基于数组的,那么切片对其值的改变,是否会影响基础数组的值呢?假设基础数组上有两个切片,他们之间值得改变会互相影响么?切片长度增加(append)会有什么影响?
我们通过代码来回答上述问题
代码1
a1 := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s1 := a1[3:6]
s2 := a1[3:7]
s1[0] = 222
fmt.Println(a1, len(a1), cap(a1), &a1)
fmt.Println(s1, len(s1), cap(s1), &s1)
fmt.Println(s2, len(s2), cap(s2), &s2)
运行结果
[0 1 2 222 4 5 6 7 8 9] 10 10
[222 4 5] 3 7
[222 4 5 6] 4 7
从结果可以看出,修改了切片1的值,基础数组和切片2对应位置的元素的值都改变了
代码2
a1 := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s1 := a1[3:6]
s2 := a1[3:7]
s1 = append(s1, []int{1111, 1112}...)
fmt.Println(a1, len(a1), cap(a1))
fmt.Println(s1, len(s1), cap(s1))
fmt.Println(s2, len(s2), cap(s2))
运行结果:
[0 1 2 3 4 5 1111 1112 8 9] 10 10
[3 4 5 1111 1112] 5 7
[3 4 5 1111] 4 7
上述结果可以看出,当切片append时,新长度没超出容量,此时对其他切片机基础数组也同样存在影响
代码3
a1 := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s1 := a1[3:6]
s2 := a1[3:7]
s1 = append(s1, []int{1111, 1112, 1113, 1114, 1115, 1116}...)
fmt.Println(a1, len(a1), cap(a1))
fmt.Println(s1, len(s1), cap(s1))
fmt.Println(s2, len(s2), cap(s2))
运行结果:
[0 1 2 3 4 5 6 7 8 9] 10 10
[3 4 5 1111 1112 1113 1114 1115 1116] 9 14
[3 4 5 6] 4 7
可以发现,此时基础数组和切片2都没收到影响,为什么会出现此现象呢?
其实是因为切片1之前的长度是3,容量是7,此时append了6个元素,即长度3+6>7,此时切片的容量不够了,需要进行扩容。而对于切片的扩容,当其容量小于1024时,容量直接翻倍,所以更新后的切片1容量为7*2=14,而切片一旦扩容,切片包括其基础数组都脱离原来的数组,变成基于新的数组的新切片,所以此时原来的基础数组及切片2均不受影响。
那么切片扩容之后长度超过了1024时怎样的呢,有兴趣的可以自己动手验证下。