Go slice容量在追加时如何变化?

时间:2019-07-18 18:17:29

标签: go slice

在服务器(当前版本为1.12.7)上运行示例The Go Tour时,如果新的切片长度大于当前支持阵列的长度,我发现切片的容量加倍到下一个2的幂。

如果我在计算机上运行相同的程序(Windows上为1.10.3版),则切片容量将更改为下一个2的倍数。

为什么它们不同?是因为Go版本还是运行时实现?容量变化确定吗?

远程服务器上的输出是

len=0 cap=0 []
len=1 cap=2 [0]
len=2 cap=2 [0 1]
len=5 cap=8 [0 1 2 3 4]

本地计算机上的输出是

len=0 cap=0 []
len=1 cap=1 [0]
len=2 cap=2 [0 1]
len=5 cap=6 [0 1 2 3 4]

这是参考代码

package main

import "fmt"

func main() {
    var s []int
    printSlice(s)

    // append works on nil slices.
    s = append(s, 0)
    printSlice(s)

    // The slice grows as needed.
    s = append(s, 1)
    printSlice(s)

    // We can add more than one element at a time.
    s = append(s, 2, 3, 4)
    printSlice(s)
}

func printSlice(s []int) {
    fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}

2 个答案:

答案 0 :(得分:0)

TL; DR:这取决于存储在数组中的元素的大小

实现可以在这里看到:

https://github.com/golang/go/blob/master/src/runtime/slice.go

但是,正如您所看到的,回顾历史并不能一直保持不变。

这也许也可以解释您在不同版本的Go上可能会注意到的区别。

进行一些测试表明,大小为0的结构如何将容量仅增加1个元素,并且每次增长时int或string都将重复,而每次增长时3字节的结构“大约”会翻倍。

您可以使用不同的类型来执行这样的代码,以查看实际情况:

arr := []struct{}{}
oldCap := 0
for i := 0; i < 100; i++ {
    arr = append(arr, struct{}{})
    if cap(arr) != oldCap {
        oldCap = cap(arr)
        fmt.Println("arr", cap(arr))
    }
}

显示上述情况的操场:

https://play.golang.org/p/OKtCFskbp2t

答案 1 :(得分:-1)

根据Go slice internal的信息,append的实现行为如下。

它只是(len(source slice) + len(new data)) * 2

的倍数
func AppendByte(slice []byte, data ...byte) []byte {
    m := len(slice)
    n := m + len(data)
    if n > cap(slice) { // if necessary, reallocate
        // allocate double what's needed, for future growth.
        newSlice := make([]byte, (n+1)*2)
        copy(newSlice, slice)
        slice = newSlice
    }
    slice = slice[0:n]
    copy(slice[m:n], data)
    return slice
}