Go 中的切片:为什么它允许追加的容量超过允许的容量?

Slices in Go: why does it allow appending more than the capacity allows?

在 Go 中制作 slicecapacity 参数对我来说意义不大。例如,

aSlice := make([]int, 2, 2) //a new slice with length and cap both set to 2
aSlice = append(aSlice, 1, 2, 3, 4, 5) //append integers 1 through 5
fmt.Println("aSlice is: ", aSlice)  //output [0, 0, 1, 2, 3, 4, 5]

如果slice允许插入的元素比capacity允许的多,为什么还要在make()函数中设置?

内置的append()函数使用指定的切片将元素附加到如果它有足够大的容量来容纳指定的元素。

但是如果传递的切片不够大,它分配一个新的、足够大的切片,将传递的切片中的元素复制到新切片并将元素附加到那个新的切片。 returns 这个新切片。引用 append() 文档:

The append built-in function appends elements to the end of a slice. If it has sufficient capacity, the destination is resliced to accommodate the new elements. If it does not, a new underlying array will be allocated. Append returns the updated slice. It is therefore necessary to store the result of append, often in the variable holding the slice itself:

make做切片时如果长度和容量相同,容量可以省略,此时默认为指定长度:

// These 2 declarations are equivalent:
s := make([]int, 2, 2)
s := make([]int, 2)

另请注意,append() 在切片的最后一个元素之后追加元素。并且上面的切片在声明之后已经有 len(s) == 2,所以如果你只向它附加 1 个元素,它会导致重新分配,如本例所示:

s := make([]int, 2, 2)
fmt.Println(s, len(s), cap(s))
s = append(s, 1)
fmt.Println(s, len(s), cap(s))

输出:

[0 0] 2 2
[0 0 1] 3 4

所以在你的例子中你应该做的是这样的:

s := make([]int, 0, 10) // Create a slice with length=0 and capacity=10
fmt.Println(s, len(s), cap(s))
s = append(s, 1)
fmt.Println(s, len(s), cap(s))

输出:

[] 0 10
[1] 1 10

如果您想更详细地了解切片,我推荐以下博客文章:

Go Slices: usage and internals

Arrays, slices (and strings): The mechanics of 'append'

主要是一个优化,并不是唯一的go,其他语言类似的结构也有这个。

当您追加的容量超过容量时,运行时需要为新元素分配更多内存。这很昂贵,还会导致内存碎片。

通过指定容量,运行时提前分配需要的东西,避免重新分配。但是,如果您事先不知道估计容量或它发生变化,则不必设置它,运行时会重新分配所需的内容并自行增加容量。