Chaque opération d'ajout vérifiera si la tranche a une capacité suffisante. Si cela est suffisant, les éléments seront ajoutés directement au tableau d'origine. et une nouvelle tranche sera renvoyée. Le tableau sous-jacent reste inchangé
et si la capacité n'est pas suffisante, un nouveau tableau sous-jacent avec une capacité suffisante sera créé. Les éléments du tableau précédent seront copiés en premier, puis les nouveaux éléments. sera ajouté à l'arrière, puis une nouvelle tranche sera renvoyée. Le tableau sous-jacent changera
La définition de capacité du nouveau tableau ici est d'augmenter selon le mécanisme de乘以2
Recommandé. tutoriels associés : "tutoriel Golang"
Et aujourd'hui, j'ai vu En ce qui concerne la structure sous-jacente du découpage Golang, à savoir Reflect.SliceHeader, nous constatons que l'expansion d'append est pas exactement une multiplication par 2. Le code source est le suivant (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 }
First Append détermine si le type est slice, puis appelle grow Expansion, à partir du jugement de l1 <= m, nous pouvons constater que lorsque la capacité est effectivement suffisante, nous créons simplement une nouvelle tranche
pour le tableau d'origine. Mais lorsque la capacité est insuffisante, nous ne pouvons le voir que lorsque l'élément actuel i0 est inférieur à. 1024 Lorsque la vitesse est 2 fois, c'est normal. Sinon, elle n'augmente que de 25% à chaque fois. La vérification du code est la suivante :
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
Après que la capacité initiale ait atteint 1024, elle n'a augmenté que de 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
Bien sûr ici Une autre confusion est que lorsque la capacité initiale est de 1023, ce n'est pas 1023×2, mais directement 1024×2. Lors du test de l'extension initiale 500, c'est aussi directement 512×2. est supposé que le niveau inférieur sera toujours complété à 2 lors de l'ajustement dynamique nième puissance, actuellement nous ne voyons que la définition de l'ajout sous le package intégré, et nous devons continuer à explorer