深入理解Golang Slice
數(shù)據(jù)結(jié)構(gòu)
type slice struct {
array unsafe.Pointer
len int
cap int
}
- slice的底層數(shù)據(jù)結(jié)構(gòu)中的array是一個指針,指向的是一個Array
- len代表這個slice的元素個數(shù)
- cap表示slice指向的底層數(shù)組容量
對slice的賦值,以值作為函數(shù)參數(shù)時,只拷貝1個指針和2個int值。
創(chuàng)建
- var []T 或 []T{}
- func make([]T,len,cap) []T
nil切片和空切片
- nil 切片被用在很多標(biāo)準(zhǔn)庫和內(nèi)置函數(shù)中,描述一個不存在的切片的時候,就需要用到 nil 切片。比如函數(shù)在發(fā)生異常的時候,返回的切片就是 nil 切片。nil 切片的指針指向 nil.
- 空切片一般會用來表示一個空集合。比如數(shù)據(jù)庫查詢,一條結(jié)果也沒有查到,那么就可以返回一個空切片。
擴容 - 計算策略
- 若 Slice cap 大于 doublecap,則擴容后容量大小為 新 Slice 的容量(超了基準(zhǔn)值,我就只給你需要的容量大?。?/li>
- 若 Slice len 小于 1024 個,在擴容時,增長因子為 1(也就是 3 個變 6 個)
- 若 Slice len 大于 1024 個,在擴容時,增長因子為 0.25(原本容量的四分之一)
擴容 - 內(nèi)存策略
- 翻新擴展:當(dāng)前元素為 kindNoPointers,也就是非指針類型,將在老 Slice cap 的地址后繼續(xù)申請空間用于擴容
- 舉家搬遷:重新申請一塊內(nèi)存地址,整體遷移并擴容
拷貝
slicecopy 方法會把源切片值(即 from Slice )中的元素復(fù)制到目標(biāo)切片(即 to Slice )中,并返回被復(fù)制的元素個數(shù),copy 的兩個類型必須一致。slicecopy 方法最終的復(fù)制結(jié)果取決于較短的那個切片,當(dāng)較短的切片復(fù)制完成,整個復(fù)制過程就全部完成了。
特性
slice的array存儲在連續(xù)內(nèi)存上,因此具有以下特點:
1. 隨機訪問很快,適合下標(biāo)訪問,緩存命中率很高;
2. 動態(tài)擴容會涉及內(nèi)存拷貝和開辟新內(nèi)存,會帶來gc壓力,內(nèi)存碎片化;
3. 如果可預(yù)估使用空間,提前分配cap的大小是極好的;
4. 新、老slice公用底層數(shù)組,對底層數(shù)組的更改都會影響到彼此;
5. append可以掰斷新老slice共用底層數(shù)組的關(guān)系;
參考資料
[1] 深入解析Go中Slice底層實現(xiàn) https://halfrost.com/go_slice/
[2] 深入解析Go Slice https://segmentfault.com/a/1190000017341615