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

我只是一个虾纸丫 提交于 2020-01-15 12:17:30

问题


The capacity parameter in making a slice in Go does not make much sense to me. For example,

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]

If the slice allows inserting more elements than the capacity allows, why do we need to set it in the make() function?


回答1:


The builtin append() function uses the specified slice to append elements to if it has a big enough capacity to accomodate the specified elements.

But if the passed slice is not big enough, it allocates a new, big enough slice, copies the elements from the passed slice to the new slice and append the elements to that new slice. And returns this new slice. Quoting from the append() documentation:

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:

When making a slice with make if the length and capacity are the same, the capacity can be omitted, in which case it is defaulted to the specified length:

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

Also note that append() appends elements after the last element of the slice. And the above slices already have len(s) == 2 right after declaration so if you append even just 1 element to it, it will cause a reallocation as seen in this example:

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

Output:

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

So in your example what you should do is something like this:

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))

Output:

[] 0 10
[1] 1 10

I recommend the following blog articles if you want to understand slices in more details:

Go Slices: usage and internals

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




回答2:


It is mainly an optimization, and it is not unique to go, similar structures in other languages have this as well.

When you append more than the capacity, the runtime needs to allocate more memory for the new elements. This is costly and can also cause memory fragmentation.

By specifying the capacity, the runtime allocates what is needed in advance, and avoids reallocations. However if you do not know the estimated capacity in advance or it changes, you do not have to set it, and the runtime reallocates what is needed and grows the capacity itself.



来源:https://stackoverflow.com/questions/28176059/slices-in-go-why-does-it-allow-appending-more-than-the-capacity-allows

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!