go数组和切片
数组:
数组固定长度。数组长度是数组类型的一部分,所以[3]int 和[4]int 是两种不 同的数组类型数组需要指定大小,不指定也会根据初始化,自动推算出大小, 大小不可改变。数组是通过值传递的
切片:
切片可以改变长度。切片是轻量级的数据结构,三个属性,指针,长度,容量 不需要指定大小切片是地址传递(引用传递)可以通过数组来初始化,也可以 通过内置函数 make()来初始化,初始化的时候 len=cap,然后进行扩容。
数组和切片在传递的时候的区别:
数组是值传递, 切片是引用传递
切片扩容
func main() {
arr := make([]int, 0)
for i := 0; i < 2000; i++ {
fmt.Println("len 为", len(arr), "cap 为", cap(arr))
arr = append(arr, i)
} }
我们可以看下结果 依次是
0,1,2,4,8,16,32,64,128,256,512,1024
但到了 1024 之后,就变成了
1024,1280,1696,2304
每次都是扩容了四分之一左右 扩容的机制:
Go 中切片扩容的策略是这样的:
首先判断,如果新申请容量大于 2 倍的旧容量,最终容量就是新申请的容量 否则判断,如果旧切片的长度小于 1024,则最终容量就是旧容量的两倍 否则判断,如果旧切片长度大于等于 1024,则最终容量从旧容量开始循环增加原来的 1/4, 直到最终容量大于等于新申请的容量 如果最终容量计算值溢出,则最终容量就是新申请容量
扩容前后的 Slice 是否相同:
情况一:
原数组还有容量可以扩容(实际容量没有填充完),这种情况下,扩容以后的 数组还是指向原来的数组,对一个切片的操作可能影响多个指针指向相同地址 的 Slice。
情况二:
原来数组的容量已经达到了最大值,再想扩容, Go 默认会先开一片内存区 域,把原来的值拷贝过来,然后再执行 append() 操作。这种情况丝毫不影响 原数组。
要复制一个 Slice,最好使用 Copy 函数。
切片的底层实现:
切片是基于数组实现的,它的底层是数组,它自己本身非常小,可以理解为对 底层数组的抽象。因为基于数组实现,所以它的底层的内存是连续分配的,效 率非常高,还可以通过索引获得数据。
切片本身并不是动态数组或者数组指针。它内部实现的数据结构通过指针引用 底层数组,设定相关属性将数据读写操作限定在指定的区域内。切片本身是一 个只读对象,其工作机制类似数组指针的一种封装。
切片对象非常小,是因为它是只有 3 个字段的数据结构: 1.指向底层数组的指针 2.切片的长度 3.切片的容量