package main
import "fmt"
func main(){
//var array [10]int
array := make([]int,10)
array = append(array,1,2,3,4,5,6,7,8,9,10)
var slice = array[5:6]
fmt.Println("slice的长度是",len(slice))
fmt.Println("slice的cap是",cap(slice))
fmt.Println(array)
fmt.Println(slice)
}
输出如下:
slice的长度是 1
slice的cap是 15
[0 0 0 0 0 0 0 0 0 0 1 2 3 4 5 6 7 8 9 10]
[0]
我们发现虽然,var slice = array[5:6],以为slice的cap是1,但是实际上slice的cap是15,也就是从5开始。
切片的底层如下,
也就是说,数组后面的内容都作为切片的预留内存,即他的capacity为从初始的位置到array结束的地方。
数组和数组的切片共享底层的存储空间,这事使用的过程中需要额外注意的地方。
查看源码包中的,slice是一个结构体,他的array指针指向底层的数组。
type slice struct {
array unsafe.Pointer
len int
cap int
}
1)使用make来创建slice
这个时候,可以同时指定长度和容量,创建的时候底层会分配一个数组,数组的长度就是他的容量。
slice := make([]int,5,10)
该slice的长度为5,即可以使用下标 slice[0] ~ slice[4]来操作里面的元素,capacity为 10,表示后续向slice添加新的元素时可以不必重新分配内存,直接使用预留内存即可,直到预留内存不足时再扩容。
2)使用数组来创建slice
使用数组创建slice时,slice将与原数组共用一部分内存。
例如,slice := array [5:7]语句所创建的slice的结构如下图所示。
切片从数组 array[5]开始,到数组 array[7]结束(不含 array[7]),即切片的长度为2,数组后面的内容都作为切片的预留内存,即capacity 为5。
数组和数组的切片共享底层存储空间,这是使用过程中需要额外注意的地方。
3 ) slice扩容
使用 append向slice追加元素时,如果slice空间不足,则会触发slice扩容,扩容实际上是重新分配一块更大的内存,将原 slice的数据拷贝进新slice,然后返回新slice,扩容后再将数据追加进去。
例如,当向一个 capacity为5且length也为5的slice再次追加1个元素时,就会发生扩容,如下图所示。
案例如下:
func main(){
//var array [10]int
array := make([]int,10)
fmt.Println("扩容前的array",array)
fmt.Println("扩容前的array的len",len(array))
fmt.Println("扩容前的array的len",cap(array))
array = append(array,1,2,3,4,5,6,7,8,9,10)
fmt.Println("扩容后的array",array)
fmt.Println("扩容后的array的len",len(array))
fmt.Println("扩容后的array的cap",cap(array))
}
扩容前的array [0 0 0 0 0 0 0 0 0 0]
扩容前的array的len 10
扩容前的array的len 10
扩容后的array [0 0 0 0 0 0 0 0 0 0 1 2 3 4 5 6 7 8 9 10]
扩容后的array的len 20
扩容后的array的cap 20
扩容操作只关心容量,会把原slice的数据拷贝到新slice中,追加数据由append在扩容结束后完成。由上图可见,扩容后新slice的长度仍然是5,但容量由5提升到了10,原slice的数据也都拷贝到了新slice指向的数组中。
4) slice拷贝
使用copy()内置函数拷贝两个切片时,会将源切片的数据逐个拷贝到目的切片指向的数组中,拷贝数量取两个切片长度的最小值。
例如长度为10的切片拷贝到长度为5的切片中时,将拷贝5个元素。也就是说,拷贝过程中不会发生扩容。