每次append 操作都會檢查slice 是否有足夠的容量,如果足夠會直接在原始數組上追加元素並返回一個新的slice,底層數組不變
而若容量不夠,會創建一個新的容量足夠的底層數組,先將之前數組的元素複製過來,再將新元素追加到後面,然後返回新的slice,底層數組改變
而這裡對新數組的容量定義是按乘以2
的機制增加
相關教程推薦:《golang教程》
而今天看到關於Golang 切片的底層結構即reflect.SliceHeader 時,發現append 的擴容並不完全是2倍增長,源碼如下(Go version 1.13):
// grow grows the slice s so that it can hold extra more values, allocating // more capacity if needed. It also returns the old and new slice lengths. func grow(s Value, extra int) (Value, int, int) { i0 := s.Len() i1 := i0 + extra if i1 <p>首先Append 判斷類型是否slice,然後呼叫grow 擴容,從l1 </p><p>但當容量不足時,可以看到只有在目前元素i0 小於1024時,才是按2倍速度正常,否則其實每次只成長25%,程式碼驗證如下:</p><pre class="brush:php;toolbar:false">func main() { str := make([]int, 1023) fmt.Println(len(str), cap(str)) str = append(str, 1) fmt.Println(len(str), cap(str)) str = append(str, 1) fmt.Println(len(str), cap(str)) } 输出: 1023 1023 1024 2048 1025 2048
初始容量已經達到1024後,只成長了256
func main() { str := make([]int, 1024) fmt.Println(len(str), cap(str)) str = append(str, 1) fmt.Println(len(str), cap(str)) str = append(str, 1) fmt.Println(len(str), cap(str)) } 输出: 1024 1024 1025 1280 1026 1280
當然這裡還有個疑惑在於,初始容量為1023時,並不是按1023×2,而是直接1024×2,測試初始500擴容也是直接按512×2,猜測更底層在動態調整時總是會補充為2的n次方,目前builtin套件下只看到append 的定義說明,還有待繼續挖掘