首頁  >  文章  >  後端開發  >  關於Golang Slice的append擴容

關於Golang Slice的append擴容

藏色散人
藏色散人轉載
2021-06-23 16:17:561641瀏覽
每次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 < i0 {
        panic("reflect.Append: slice overflow")
    }
    m := s.Cap()
    if i1 <= m {
        return s.Slice(0, i1), i0, i1
    }
    if m == 0 {
        m = extra
    } else {
        for m < i1 {
            if i0 < 1024 {
                m += m
            } else {
                m += m / 4
            }
        }
    }
    t := MakeSlice(s.Type(), i1, m)
    Copy(t, s)
    return t, i0, i1
}

// Append appends the values x to a slice s and returns the resulting slice.
// As in Go, each x's value must be assignable to the slice's element type.
func Append(s Value, x ...Value) Value {
    s.mustBe(Slice)
    s, i0, i1 := grow(s, len(x))
    for i, j := i0, 0; i < i1; i, j = i+1, j+1 {
        s.Index(i).Set(x[j])
    }
    return s
}

首先Append 判斷類型是否slice,然後呼叫grow 擴容,從l1 <= m 的判斷可以發現確實容量足夠的情況下,只是對原始數組建立一個新的slice

但當容量不足時,可以看到只有在目前元素i0 小於1024時,才是按2倍速度正常,否則其實每次只成長25%,程式碼驗證如下:

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 的定義說明,還有待繼續挖掘

以上是關於Golang Slice的append擴容的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:segmentfault.com。如有侵權,請聯絡admin@php.cn刪除