slice切片,今天来说说golang的第二个引用数据类型slice,上次说到引用类型的变量传递的是引用的指针但是拷贝之后的地址还是相同的,导致修改任何一个拷贝都会改变指针指向的地址的实际值。chan的底层数据结构是hchan,那么slice的底层指向的是什么结构呢?slice被叫做动态数组,其实slice的底层数据结
构指向数组结构,看一下源码src/runtime/slice.go:slice
type slice struct {
array unsafe.Pointer //指向底层数组的指针
len int //切片的长度
cap int //底层数组的容量
}
因为golang的数组是固定长度的,所以用起来还是很不方便的,slice就是对数组进行封装,实现屏蔽底层数组,在底层数组容量不足时进行自动扩容分配内存生成新的底层数组支撑slice用户层面的动态长度。
创建slice,make([]int, 5, 10) 5就是slice初始化的长度,可以通过0-4的下标区操作slice;10就是底层数组的cap,继续append元素可以不重新分配内存,直接使用预留的内存;还能使用现有的数组创建slice,slice := array[5:7],这样slice的底层指向这个数组修改slice也会修改原数组;切片基础上创建切片sliceC := sliceA[0:5:5]
slice的扩容, append操作会引起slice的扩容,在cap的空间不足时去分配更大的内存,将原来底层数组的数据拷贝一份到新的内存数组,然后将append的数据追加进去;例如make([]int, 5, 10),当slice长度为5时可以继续append元素,当长度为10,再append元素会引起底层数组的扩容然后len++。数组生成的切片的cap和底层数组的cap相同,但是发生截取slice := array[start:end]生成的slice的cap也就得len(array)-start。如果切片的容量小于1024个元素,那么扩容的时候slice的cap*2就是2倍;一旦元素个数超过1024个元素,增长因子就变成1.25,即每次增加原来容量的1/4。
常用使用demo:
func main(){
var array [10]int
var slice = array[5:6]
fmt.Println("lenth of slice: ", len(slice))
fmt.Println("capacity of slice: ", cap(slice))
fmt.Println(&slice[0] == &array[5])
}
//lenth of slice: 1
//capacity of slice: 5
//true
//slice根据array创建共享数组的存储空间,slice起始位置是array[5],长度为1,容量为5, slice[0]和array[5]地址相同。
func main() {
var slice []int
slice = append(slice, 1, 2, 3)
newSlice := AddElement(slice, 4)
fmt.Println(&slice[0] == &newSlice[0])
}
func AddElement(slice []int, e int) []int {
return append(slice, e)
}
//true
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