Golang Sliceの追記拡張について

藏色散人
リリース: 2021-06-23 16:17:56
転載
1699 人が閲覧しました
各追加操作では、スライスに十分な容量があるかどうかがチェックされ、十分な場合、要素は元の配列に直接追加されます新しいスライスが返されます。基になる配列は変更されません。
容量が十分でない場合は、十分な容量を持つ新しい基になる配列が作成されます。前の配列の要素が最初にコピーされ、次に新しい要素がコピーされます。が最後に追加されると、新しいスライスが返されます。基になる配列が変更されます
新しい配列の容量は、ここで に 2 を掛けて定義されます。

関連するチュートリアルの推奨事項: "golang チュートリアル"

今日、Golang スライスの基礎となる構造、つまり、reflect.SliceHeader を見たところ、append の展開が正確には一致していないことがわかりました。ソースコードは以下の通りです(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で型を決定し、スライスするかどうかを判断し、growを呼び出して容量を拡張します l1の判定より<= m の場合、容量が実際に十分である場合は、元の配列に対して新しいスライス

を作成するだけであることがわかります。しかし、容量が不十分な場合は、現在の要素 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 に達した後は、増加するのは 25% だけです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 乗に加算されます。現在、組み込みパッケージの下にある append の定義のみが確認されており、引き続き調査する必要があります。

以上がGolang Sliceの追記拡張についての詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:segmentfault.com
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート