简体   繁体   English

为什么在 Golang 中创建切片时到底有一个 CAPACITY 参数

[英]Why exactly is there a CAPACITY parameter when creating a slice in Golang

This is a pretty straightforward question:这是一个非常简单的问题:

If the capacity of a slice in Golang can be exceeded, why is there a capacity parameter in the first place?如果Golang中一个切片的容量可以超过,那为什么首先会有容量参数?

I reckon this has to do with the memory management , some kind of "knowing where to allocate the slice in memory" but I don't know exactly.我认为这与内存管理有关,某种“知道在内存中分配切片的位置”,但我不确切知道。

If the capacity of a slice in Golang can be exceeded, why is there a capacity parameter in the first place? 如果可以超过Golang中一个切片的容量 ,为什么首先要有一个容量参数?

I don't know what you mean by this, but the capacity can't be exceeded. 我不知道您的意思是什么,但是容量不能超过。 Slices can be indexed up to its length (exclusive) which can't exceed the capacity, and they can be re-sliced up to its capacity (inclusive). 可以在不超过容量的长度(不包括其长度)的范围内对切片进行索引,并且可以根据其容量(包括)进行重新切片。

Spec: Index expressions: 规格:索引表达式:

A primary expression of the form a[x] a[x]形式a[x]主表达式

If a is not a map: ...the index x is in range if 0 <= x < len(a) , otherwise it is out of range 如果a不是地图:...如果0 <= x < len(a) ,则索引x 在范围内 ,否则它不在范围内

And Spec: Slice expressions: 规范:切片表达式:

...the primary expression: a[low : high] ...主要表达式: a[low : high]

For arrays or strings, the indices are in range if 0 <= low <= high <= len(a) , otherwise they are out of range . 对于数组或字符串,如果0 <= low <= high <= len(a) ,则索引在范围内 ,否则它们超出范围 For slices, the upper index bound is the slice capacity cap(a) rather than the length. 对于切片,索引的上限是切片容量cap(a)而不是长度。

And also: 并且:

...the primary expression: a[low : high : max] ...主要表达式: a[low : high : max]

The indices are in range if 0 <= low <= high <= max <= cap(a) , otherwise they are out of range . 如果0 <= low <= high <= max <= cap(a) ,则索引在范围内 ,否则它们超出范围

You may provide the capacity to the builtin make() thinking of future growth, so fewer allocations will be needed should you need to append elements to it or should you need to reslice it. 您可以为内置的make()考虑未来增长的功能,因此,如果您需要向其添加元素或需要对其进行切片,则将需要较少的分配。 The builtin append() just reslices the slice you append to if it has enough capacity for the additional elements, but it has to allocate a new backing array (and copy the existing contents into it) if it has no room for the new elements. 内置的append()只是在附加的切片具有足够的附加元素容量时对其切片,但是如果它没有足够的空间容纳新的元素,则必须分配一个新的支持数组(并将现有内容复制到其中)。 append() will return the new slice which may or may not point to the original backing array. append()将返回可能指向也可能指向原始后备数组的新切片。

Let's see an example. 让我们来看一个例子。 Let's create a slice with 0 length and capacity, and append 10 elements to it. 让我们创建一个长度和容量为0的切片,并向其添加10个元素。 And to see when a new reallocation happens, we also print the address of its first element (the 0th element): 为了查看何时发生新的重新分配,我们还打印了其第一个元素(第0个元素)的地址:

fmt.Println("With 0 capacity")
s := make([]int, 0)
for i := 0; i < 10; i++ {
    s = append(s, i)
    fmt.Println(i, &s[0])
}

This outputs: 输出:

With 0 capacity
0 0x416030
1 0x416030
2 0x416040
3 0x416040
4 0x452000
5 0x452000
6 0x452000
7 0x452000
8 0x434080
9 0x434080

As you can see, a new backing array is allocated when we appended the third ( i=2 ), fifth ( i=4 ) and ninth elements ( i=8 ), and also when we appended the first element (as the original backing array could not hold any elements). 如您所见,当我们添加第三个( i=2 ),第五个( i=4 )和第九个元素( i=8 ),以及我们添加第一个元素(作为原始后备)时,将分配一个新的后备数组数组不能包含任何元素)。

Now let's repeat the above example when we create the initial slice with again length = 0 but with capacity = 10: 现在,当我们再次创建长度为0但容量为10的初始切片时,让我们重复上面的示例:

fmt.Println("With 10 capacity")
s = make([]int, 0, 10)
for i := 0; i < 10; i++ {
    s = append(s, i)
    fmt.Println(i, &s[0])
}

Now output will be: 现在输出将是:

With 10 capacity
0 0x44c030
1 0x44c030
2 0x44c030
3 0x44c030
4 0x44c030
5 0x44c030
6 0x44c030
7 0x44c030
8 0x44c030
9 0x44c030

As you can see, the address of the first element never changed, which means there were no new backing array allocations happening in the background. 如您所见,第一个元素的地址从未改变,这意味着在后台没有新的后备数组分配发生。

Try the example on the Go Playground . Go Playground上尝试该示例。

To increase the capacity of a slice one must create a new, larger slice and copy the contents of the original slice into it. 要增加切片的容量,必须创建一个更大的新切片,并将原始切片的内容复制到其中。

If the capacity of a slice in Golang can be exceeded, why is there a capacity parameter in the first place? 如果可以超过Golang中一个切片的容量,为什么首先要有一个容量参数?

Suppose we know ahead of time the capacity of slice, then we can use capacity parameter of make built in function to allocate memory instead of increasing capacity of slice dynamically using append which is not that memory efficient. 假设我们提前知道切片的容量,那么我们可以使用内置函数的容量参数来分配内存,而不是使用附加的动态增加切片的容量来提高内存的效率。

so in below example 所以在下面的例子中

type Element struct {
    Number int
}

func main() {
    Max := 100000
    startTime := time.Now()

    // Capacity given
    elements1 := make([]Element, Max, Max)
    for i := 0; i < Max; i++ {
        elements1[i].Number = i
    }
    elapsedTime := time.Since(startTime)
    fmt.Println("Total Time Taken with Capacity in first place: ", elapsedTime)

    startTime = time.Now()
    // Capacity not given
    elements2 := make([]Element, 0)
    for i := 0; i < Max; i++ {
        elements2 = append(elements2, Element{Number: i})
    }
    elapsedTime = time.Since(startTime)
    fmt.Println("Total Time Taken without capacity: ", elapsedTime)
}

output

Total Time Taken with Capacity in first place:  121.084µs
Total Time Taken without capacity:  2.720059ms

Time Taken to build slice with capacity at first place is less that to build dynamically 首先构建具有容量的切片所需的时间少于动态构建所需的时间

So to answer you question, capacity parameter is in the first place for better performance and memory efficiency 因此,要回答您的问题,首先要考虑容量参数,以获得更好的性能和内存效率

There are many excellent posts in the golang blog. golang博客中有许多出色的文章。 This one on slices , will give you a detailed insight into the underlying implementation of slices and how capacity works. 片上的这一部分将使您详细了解片的底层实现以及容量如何工作。

The post goes through how the append operation works and how the capacity value lets append know whether to reuse the underlying memory array, or allocate a bigger array when more capacity is needed. 文章介绍了append操作的工作方式以及容量值如何使append知道是否重用基础内存阵列,还是在需要更多容量时分配更大的阵列。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM