모든 추가 작업은 슬라이스에 충분한 용량이 있는지 확인합니다. 용량이 충분하면 요소가 원래 배열에 직접 추가되고 새 슬라이스가 반환됩니다. 기본 배열은 그대로 유지됩니다
그리고 용량이 충분하지 않으면 용량이 충분한 새 기본 배열이 생성됩니다. 먼저 이전 배열의 요소를 복사하고 새 요소를 뒤에 추가한 다음 새 요소를 반환합니다. 기본 배열이 변경되고
여기서 새 배열의 용량 정의는 다음과 같습니다.乘以2
메커니즘이 증가합니다
추천 관련 튜토리얼: "golang 튜토리얼"
Golang 슬라이싱의 기본 구조를 봤을 때, Reflect.SliceHeader는 오늘,append의 확장이 정확히 2배 증가가 아니라는 것을 발견했습니다. 소스 코드는 다음과 같습니다(Go 버전 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가 유형이 슬라이스인지 여부를 판별한 다음, 용량을 확장하기 위해 호출이 증가합니다. l1
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×2가 아니라 직접 1024×2로 초기 500 확장을 테스트해보면 바로 512×2입니다. 낮은 레벨은 동적으로 조정되는 것으로 추측됩니다. 항상 2의 n제곱에 추가됩니다. 현재는 내장 패키지 아래에 추가 정의만 표시되며 계속해서 살펴봐야 합니다