golang中的数组和其他语言的数组概念差别不大。golang的数组是定长的,初始化之后大小不能发生改变。需要使用变长数组的场景,就需要引入切片
切片是某个数组的引用,切片的容量可以动态变化
切片
切片中的几个概念:
- 元素(element):指切片中的数据
- 长度(len):切片中元素的个数
- 容量(cap):切片的最大元素个数
append
golang中对append的说明如下:
The append built-in function appends elements to the end of a slice. If it has sufficient capacity, the destination is resliced to accommodate the new elements. If it does not, a new underlying array will be allocated. Append returns the updated slice. It is therefore necessary to store the result of append, often in the variable holding the slice itself
里面的关键信息在于:append的时候,如果切片的容量充足,会对切片重新切片(切片指向的是同一个底层数组),如果容量不足,会分配新的底层数组,并且复制里面的数据
下面通过代码测试一下(golang版本:go1.16,4)
import "log"
func main() {
a := [3]string{"A", "B", "C"}
b := a[1:3]
log.Printf("a is %v,b is %v, len(b) is %d, cap(b) is %d", a, b, len(b), cap(b))
b[0] = "D"
log.Printf("a is %v,b is %v, len(b) is %d, cap(b) is %d", a, b, len(b), cap(b))
}
运行结果
2021/09/16 09:25:02 a is [A B C],b is [B C], len(b) is 2, cap(b) is 2
2021/09/16 09:25:02 a is [A D C],b is [D C], len(b) is 2, cap(b) is 2
在此例中,a是一个数组,b是一个切片,容量为2,长度为2。b[0]指向a[1]。对b进行修改会影响a
增加元素
golang中通过append往切片中追加元素
b = append(b,"D")
b[0] = "E"
运行结果
2021/09/16 09:52:29 a is [A D C],b is [E C D], len(b) is 3, cap(b) is 4
执行append之后,b中将会有3个元素,超过了原有的cap,b需要扩容。扩容后容量由2变为了4,并且指向了新的底层数组,对b进行修改,不再影响a
删除元素
golang没有提供直接删除切片元素的接口,删除元素通常也是由append实现
package main
import "log"
func main() {
a := [3]string{"A", "B", "C"}
b := a[1:3]
log.Printf("a is %v,b is %v, len(b) is %d, cap(b) is %d", a, b, len(b), cap(b))
//删除元素
b = append(b[:0], b[1:]...)
log.Printf("a is %v,b is %v, len(b) is %d, cap(b) is %d", a, b, len(b), cap(b))
}
运行结果
2021/09/16 10:04:43 a is [A B C],b is [B C], len(b) is 2, cap(b) is 2
2021/09/16 10:04:43 a is [A C C],b is [C], len(b) is 1, cap(b) is 2
append之后,b的元素只剩一个C,小于b原有的容量2,所以b的容量不变(不会缩容),b的底层数组仍然是a,b[0]指向a[1]。b[0]变成了C,所以a[1]也变成了C
切片的切片
表面上看,上面的解释已经很合理了,其实遗漏了一个隐藏的细节
b = append(b[:0], b[1:]...)
append传参传入的是b[:0],是一个新的切片,append中判断是否需要扩容应该是根据b[:0]的容量大小而不是b的容量大小。那么b[:0]的容量是多少?
写段代码测试一下
c := b[:0]
log.Printf("c is %v,len(c) is %d, cap(c) is %d", c, len(c), cap(c))
d := b[1:]
log.Printf("d is %v,len(d) is %d, cap(d) is %d", d, len(d), cap(d))
运行结果
2021/09/16 13:01:09 c is [],len(c) is 0, cap(c) is 2
2021/09/16 13:01:09 d is [C],len(d) is 1, cap(d) is 1
从结果中可以看出,c从b[0]开始切片,容量是2(0->数组末尾);d从b[1]开始,容量是1(1->数组末尾)