slice 切片,因为其可以方便的进行扩容、传递等,在实际应用中比数组更加灵活。切片的一些基础使用可以看下之前的文章,传送门。
这里我们再更加深层次的了解下 slice 。先看几段代码,测试下诸位对 slice 的理解程度。
package main
import (
"fmt"
)
func main() {
var array [20]int
var slice = array[10:11]
fmt.Println("lenth: ", len(slice))
fmt.Println("capacity: ", cap(slice))
fmt.Println(&slice[0] == &array[10])
}
答 案 是:
lenth: 1
capacity: 10
true
slice根据数组array创建,与数组共享存储空间,slice起始位置是array[5],长度为1,容量为5, slice[0]和array[5]地址相同。
package main
import (
"fmt"
)
func main() {
orderLen := 5
order := make([]uint16, 2 * orderLen)
pollorder := order[:orderLen:orderLen]
lockorder := order[orderLen:][:orderLen:orderLen]
fmt.Println("len(pollorder) = ", len(pollorder))
fmt.Println("cap(pollorder) = ", cap(pollorder))
fmt.Println("len(lockorder) = ", len(lockorder))
fmt.Println("cap(lockorder) = ", cap(lockorder))
}
答 案 是:
len(pollorder) = 5
cap(pollorder) = 5
len(lockorder) = 5
cap(lockorder) = 5
[low:high:max]
意思是对 order 进行切片,新切片范围是[low, high)
,新切片容量是 max;源码包 src/runtime/slice.go 定义的 slice 数据结构为:
type slice struct {
array unsafe.Pointer
len int
cap int
}
make 创建 slice 可以同时指定其长度和容量,底层会分配一个数组,数组的长度即容量。
slice = make([]int,5,10): 表示改 slice 长度为5,容量为10。
使用数组来创建 slice 时,slice 与原数组共用一部分内存。 slice := array[5:7] 所创建的 slice ,结构如下图:
切片从数组 array[5] 开始,到数组 array[7] 结束(不包含array[7]),切片长度为 2,数组后面的内容作为切片的预留内存,即容量为5。
在使用 append 向 slice 追加元素时,若 slice 空间不足则会发生扩容,扩容会重新分配一块更大的内存,将原 slice 拷贝到新 slice ,然后返回新 slice。扩容后再将数据追加进去。
扩容操作只只对容量,扩容后的 slice 长度不变,容量变化规则如下:
使用 copy() 内置函数拷贝两个切片时,会将源切片的数据逐个拷贝到目的的切片指向的数组中,拷贝数量取「两个切片长度的最小值」。
例如将长度为10的切片拷贝到长度为5的切片时,将会拷贝5个元素。也就是说 「copy 不会发生扩容」。