golang slice append 後 capacity 增加的算法

一道題目:

圖片描述

append函數

函數定義:func append(slice []Type, elems ...Type) []Type
函數說明:內建函數append追加一個或多個elems到一個slice依賴的array的末尾,若是這個slice有足夠的capacity,則reslice以容納新增元素;若是capacity空間不夠,則從新分配內存保存新的slice依賴的array,函數返回更新後的slice.(slice是引用,array保存真正的數據,slice切片理解:https://blog.haohtml.com/arch... )html

注意:append不會修改傳參進來的slice(len和cap),只會在不夠用的時候新分配一個array,並把以前的slice依賴的array數據拷貝過來;因此對同一個slice 重複 append,只要不超過cap,都是修改的同一個array,後面的會覆蓋前面golang

capacity如何增加?

圖片裏提到"Capacity grows in a way not related to the size of appending data",那麼,增加算法是什麼樣的呢?看源代碼 src/runtime/slice.go:算法

newcap := old.cap
    doublecap := newcap + newcap
    if 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
            }
        }
    }
  1. 先將舊的slice容量乘以2,若是乘以2後的容量仍小於新的slice容量,則取新的slice容量(append多個elems)
  2. 若是新slice小於等於舊slice容量的2倍,則取舊slice容量乘以2
  3. 若是舊的slice容量大於1024,則新slice容量取舊slice容量乘以1.25

代碼後面還會對newcap進行roundup,好比在64位平臺,newcap是奇數的話就會+1app

回頭看題

package main

import (
    "fmt"
)
func main(){
    a := []int{1,2}
    b := append(a,3)
    fmt.Printf("cap(a)=%v,cap(b)=%v\n",cap(a),cap(b))
    c := append(b,4)
    d := append(b,5)
    fmt.Printf("cap(a)=%v,cap(b)=%v\n",cap(a),cap(b))
    fmt.Println(a,b,c,d)
    fmt.Printf("%p,%p,%p,%p\n",a,b,c,d)
}

第一次append,超出了a的cap範圍,分配一個新的newcap爲oldcap*2的array,即4;a不變
第二次append,len(b)是3,cap(b)是4,沒有超出b的cap範圍,b所依賴的array在len(b)的位置追加4,c共用這個array;b不變
第三次append,因爲b沒有變化,b所依賴的array在len(b)的位置追加5,會覆蓋上一步的4
因此:c[3]和d[3]引用的是同一塊內存,都是5函數

另外,若是 d:= append(c,5) 則結果就是 c[3]=4,d[3]=4,d[4]=5由於這一步會新分配array,並將c的數據拷貝過來oop

總結

寫得比較匆忙,若有紕漏,歡迎指正。spa

相關文章
相關標籤/搜索