Go 切片
切片(slice)是对数组的一个连续片段的引用
切片VS数组
- 数组定长,定义的时候就需要确定;切片不定长,append时会自动扩容
- 数组的零值是元素类型的零值,切片的零值是nil
- 数组是值类型,切片是引用类型
- 数组的赋值形式为值传递,切片的赋值形式为引用传递
结构
type slice struct {
array unsafe.Pointer
len int
cap int
}
零切片:
内部数组的元素都是零值或者底层数组的内容就全是 nil 的切片
创建:
使用make创建的、长度、容量都不为0的切片
slice := make([]int,5)
nil切片:
长度和容量都为0,并且和nil比较的结果为true的切片
创建:
直接创建切片的方式、new创建切片的方式
var slice []int、var slice = *new([]int)
空切片:
空切片的长度和容量也都为0,但是和nil的比较结果为false,因为所有的空切片的数据指针都指向同一个地址 0xc42003bda0;
创建:
使用字面量、make可以创建空切片
var slice = []int{}、var slice = make([]int, 0)
如果要创建长度容量为 0 的切片,官方推荐 nil 切片
slice不支持并发读写,不是线程安全的。
线程安全:
1.加锁:适合对性能要求不高的场景
2.通过channel实现:适合对性能要求高的场景
多个切片如果共享同一个底层数组,对一个切片或者底层数组的更改会影响其他切片
扩容
触发:
当slice append时,当slice的cap不足以容纳新元素
原理:
1.当新申请容量比两倍原有容量大,扩容后容量大小 = 新申请容量
2.如果原有slice长度<1024,每次扩容为原来的2倍
3.如果原有slice长度>=1024,诶次扩容为原来的1.25倍
原因:
case1:slice a很大,全局变量slice b引用了小部分a,那么a未被引用的部分就不会被释放
case2:slice a引用小部分 很大的数组b,b无法被回收
避免:
将需要的分片复制到一个新的slice中,减少内存的占用
其他
将slice元素循环向左旋转n个元素的方法
三次调用reverse反转函数,第一次是反转开头的n个元素,然后是反转剩下的元素,最后是反转整个slice的元素。
reverse(s[:n])、reverse(s[n:])、reverse(s)