什么是切片
切片就是我们经常所说的动态数组,可以灵活的增加和减少切片内的元素。
常见的切片操作有:reslice、append、copy
切片和数组该如何选择呢?
区别
切片是引用类型,数组是值类型。那么数组在复制和函数传参时候都是值复制。
优缺点
数组作为函数传参,当数组比较大时候往往会造成资源浪费。
切片是引用传递,所以它们不需要使用额外的内存并且比使用数组更有效率。
底层的数据结构
首先切片本身并不是动态数组或者数组指针,它是通过指针引用底层数组,本身是一个只读对象,其工作机制类似数组指针的一种封装。
切片数据结构定义:
type slice struct {
array unsafe.Pointer
len int
cap int
}
切片的结构体由3部分构成,Pointer 是指向一个数组的指针,len 代表当前切片的长度,cap 是当前切片的容量。cap 总是大于等于 len 的。
创建切片
两种形式:make 创建切片,空切片。
- make
slice := make([]int,4,6)
创建了一个len是4,容量是6的切片。、
字面量也可以创建切片
slice := []int{1,2,3,4,5}
需要注意的是 [ ] 里面不要写数组的容量,因为如果写了个数以后就是数组了,而不是切片了。
- 空切片
var slice []int
切片扩容
一个是扩容时候的策略,还有一个就是扩容是生成全新的内存地址还是在原来的地址后追加。
扩容不像java那样通过负载因子触发,具体策略如下:
- 首先判断,如果新申请容量(cap)大于2倍的旧容量(old.cap),最终容量(newcap)就是新申请的容量(cap)
- 否则判断,如果旧切片的长度小于1024,则最终容量(newcap)就是旧容量(old.cap)的两倍,即(newcap=doublecap)
- 否则判断,如果旧切片长度大于等于1024,则最终容量(newcap)从旧容量(old.cap)开始循环增加原来的 1/4,即(newcap=old.cap,for {newcap += newcap/4})直到最终容量(newcap)大于等于新申请的容量(cap),即(newcap >= cap)
- 如果最终容量(cap)计算值溢出,则最终容量(cap)就是新申请容量(cap)
切片拷贝
完整复制一个切片,
array := []int{1, 2, 3, 4}
slice := make([]int, len(array))
n := copy(slice, array)
fmt.Println(n,slice)