切片实现原理
切片,则是由一个指向数组的指针,切片的长度和容量两个int组成
切片内存分配切片的长度就是它所包含的元素个数。
切片的容量是从它的第一个元素开始数,到其底层数组元素末尾的个数。
区分空切片和nil切片
nil切片空切片
一個零值的slice等於nil。一個nil值的slice併沒有底層數組。一個nil值的slice的長度和容量都是0,但是也有非nil值的slice的長度和容量也是0的,例如[]int{}或make([]int, 3)[3:]。與任意類型的nil值一樣,我們可以用[]int(nil)類型轉換表達式來生成一個對應類型slice的nil值。
var s []int // len(s) == 0, s == nil
s = nil // len(s) == 0, s == nil
s = []int(nil) // len(s) == 0, s == nil
s = []int{} // len(s) == 0, s != nil
如果你需要測試一個slice是否是空的,使用len(s) == 0來判斷,而不應該用s == nil來判斷。除了和nil相等比較外,一個nil值的slice的行爲和其它任意0産長度的slice一樣;例如reverse(nil)也是安全的。除了文檔已經明確説明的地方,所有的Go語言函數應該以相同的方式對待nil值的slice和0長度的slice。
內置的make函數創建一個指定元素類型、長度和容量的slice。容量部分可以省略,在這種情況下,容量將等於長度。
make([]T, len)
make([]T, len, cap) // same as make([]T, cap)[:len]
在底層,make創建了一個匿名的數組變量,然後返迴一個slice;隻有通過返迴的slice才能引用底層匿名的數組變量。在第一種語句中,slice是整個數組的view。在第二個語句中,slice隻引用了底層數組的前len個元素,但是容量將包含整個的數組。額外的元素是留給未來的增長用的。
切片的扩容原理
go中数组不可扩容,跳过不做讨论。slice扩容规则如下:
- 如果切片的容量小于1024个元素,那么扩容的时候slice的cap就翻番,乘以2;一旦元素个数超过1024个元素,增长因子就变成1.25,即每次增加原来容量的四分之一。
- 如果扩容之后,还没有触及原数组的容量,那么,切片中的指针指向的位置,就还是原数组,如果扩容之后,超过了原数组的容量,那么,Go就会开辟一块新的内存,把原来的值拷贝过来,这种情况丝毫不会影响到原数组。
我们用一个例子来加深理解,请说出输出结果:
package main
import (
"fmt"
)
func main() {
arr := [4]int{10, 20, 30, 40}
slice := arr[0:2]
testSlice1 := slice
testSlice2 := append(append(append(slice, 1), 2), 3)
slice[0] = 11
fmt.Println(testSlice1[0])
fmt.Println(testSlice2[0])
}
根据第二条规则,append三个元素后,slice超出了原本的arr的长度,已经变成了一个新的切片,所以slice[0]=11
不再能影响testSlice2,输出结果:
11
10
更新slice变量不仅对调用append函数是必要的,实际上对任何可能导致长度、容量或底层数组变化的操作都是必要的
引用
欢迎大家关注我的公众号
半亩房顶
网友评论