[Go] Slice的底層自動擴容


go語言聖經中的解釋:

數組和slice之間有着緊密的聯系。

一個slice是一個輕量級的數據結構,提供了訪問數組子序列(或者全部)元素的功能,而且slice的底層確實引用一個數組對象。

一個slice由三個部分構成:指針、長度和容量。

指針指向第一個slice元素對應的底層數組元素的地址,要注意的是slice的第一個元素並不一定就是數組的第一個元素。

長度對應slice中元素的數目;長度不能超過容量

容量一般是從slice的開始位置到底層數據的結尾位置。內置的len和cap函數分別返回slice的長度和容量。

 

make([]T, len, cap)    len<=cap

在底層,make創建了一個匿名的數組變量,然后返回一個slice

slice只引用了底層數組的前len個元素,但是容量將包含整個的數組。額外的元素是留給未來的增長用的

 

從下面這個自定義函數里可以了解擴容機制:

func appendInt(x []int, y int) []int {
    var z []int
//+1個元素后新的長度
    zlen := len(x) + 1
//長度小於原來的容量大小
    if zlen <= cap(x) {
        // 還有剩余空間,直接可以拿到給新的slice
        z = x[:zlen]
    } else {
        // 沒有足夠的空間,給新的slice分配原來二倍的空間
        zcap := zlen
        if zcap < 2*len(x) {
            zcap = 2 * len(x)
        }
        z = make([]int, zlen, zcap)
        copy(z, x) //把舊的slice復制到新的slice
    }
    z[len(x)] = y
    return z
}

 

通過在每次擴展數組時直接將長度翻倍從而避免了多次內存分配,也確保了添加單個元素操的平均時間是一個常數時間

 

func main() {
    var x, y []int
    for i := 0; i < 10; i++ {
        y = appendInt(x, i)
        fmt.Printf("%d cap=%d\t%v\n", i, cap(y), y)
        x = y
    }
}
每一次容量的變化都會導致重新分配內存和copy操作,當容量不夠的時候,會有一個翻倍擴充

0  cap=1    [0]
1  cap=2    [0 1]
2  cap=4    [0 1 2]
3  cap=4    [0 1 2 3]
4  cap=8    [0 1 2 3 4]
5  cap=8    [0 1 2 3 4 5]
6  cap=8    [0 1 2 3 4 5 6]
7  cap=8    [0 1 2 3 4 5 6 7]
8  cap=16   [0 1 2 3 4 5 6 7 8]
9  cap=16   [0 1 2 3 4 5 6 7 8 9]

 

向切片新增一個元素時,若該切片容量已滿,會首先根據切片容量進行判斷,小於1024字節擴容為原有容量的2倍,大於1024字節擴容為原有容量的1.25倍

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM