Go中切片扩容原理
廢話不多說(shuō)直接看源碼
// src/runtime/slice.go func growslice(et *_type, old slice, cap int) slice { // ...省略部分newcap := old.capdoublecap := newcap + newcapif cap > doublecap {newcap = cap} else {if old.len < 1024 {newcap = doublecap} else {// Check 0 < newcap to detect overflow// and prevent an infinite loop.for 0 < newcap && newcap < cap {newcap += newcap / 4}// Set newcap to the requested cap when// the newcap calculation overflowed.if newcap <= 0 {newcap = cap}}} // ...省略部分 }四個(gè)重要的屬性
- cap : 需要的容量
- old.cap : 舊切片的容量
- newcap : 最終要申請(qǐng)的容量
- doublecap : old.cap的兩倍
看代碼就知道下方的擴(kuò)容的機(jī)制
- 當(dāng)需要的容量cap大于兩倍舊容量doublecap時(shí), 我們申請(qǐng)的新容量就是需要的容量
- 當(dāng)需要的容量cap小于兩倍舊容量doublecap時(shí), 判斷是否舊切片的長(zhǎng)度小于1024, 如果小于1024, 那么newcap = 兩倍舊cap, 直接翻倍
- 當(dāng)舊切片的長(zhǎng)度 >= 1024時(shí), 會(huì)反復(fù)地增加25%,直到新容量newcap超過(guò)所需要的容量cap。 其中newcap > 0 是防止int類型溢出, 如果溢出那么就直接newcap = cap(需要的容量)
一點(diǎn)個(gè)人思考:
如何求得所需要的容量cap ?
咱們來(lái)看兩個(gè)例子
第一個(gè) :
func main() {arr := []int{1, 2, 3, 4}fmt.Println("擴(kuò)容前容量是 : ", cap(arr))arr = append(arr, []int{5, 6, 7, 8, 9}...)fmt.Println("擴(kuò)容后容量是 : ", cap(arr)) }擴(kuò)容前的容量是 4
當(dāng)我們追加了5個(gè)元素之后, 按理來(lái)說(shuō)需要的容量應(yīng)該是9 對(duì)不對(duì) ?
姑且算是cap = 9
在我們上面的growslice函數(shù)中, 如果按照cap是9來(lái)算的話
最后的newcap 應(yīng)該是 等于 9
但是打印結(jié)果是
再來(lái)看另外一個(gè)例子
對(duì)于這個(gè)自定義切片的結(jié)果就是9
結(jié)論 : 所以我推測(cè), golang內(nèi)部對(duì)于內(nèi)置類型, 有著自己的處理方式, 所以第一個(gè)例子我們對(duì)于int類型的切片append, 應(yīng)該是進(jìn)來(lái)的cap就是10, 在其他地方做了處理, 可能方便于內(nèi)存對(duì)齊.
對(duì)我我們自定義類型的切片就是直接 cap = oldcap + append()函數(shù)中追加的容量
總結(jié)
- 上一篇: 从浏览器输入URL到最终看到页面, 这其
- 下一篇: Go中的Map实现机制