在Golang中创建切片时,为什么确切地存在CAPACITY参数

时间:2019-02-20 08:42:34

标签: go slice

这是一个非常简单的问题:

如果可以超出Golang中某个切片的容量,为什么首先要有一个容量参数?

我认为这与内存管理有关,即某种“知道在内存中分配切片的位置”,但我不完全知道。

3 个答案:

答案 0 :(得分:9)

  

如果可以超出Golang中某个切片的容量,为什么首先要有一个容量参数?

我不知道您的意思是什么,但是容量不能超过。可以在不超过容量的长度(不包括其长度)的范围内对切片进行索引,并且可以对其容量(包括在内)的容量进行重新切片。

Spec: Index expressions:

  

a[x]形式的主表达式

     

如果a不是地图:...如果x,则索引0 <= x < len(a)在范围内 否则是不在范围内

还有Spec: Slice expressions:

  

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

     

对于数组或字符串,如果0 <= low <= high <= len(a),则索引在范围内,否则,它们在范围之外。对于切片,索引的上限是切片容量cap(a)而不是长度。

还有:

  

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

     

如果0 <= low <= high <= max <= cap(a),则索引在范围内;否则,它们超出范围

您可以为内置make()提供容量,以考虑未来的增长,因此,如果您需要向其追加元素或需要对其进行切片,则将需要较少的分配。内置的append()仅在切片具有足够的额外元素容量时才对其切片,但是如果它没有空间容纳新切片,则必须分配新的后备数组(并将现有内容复制到其中)元素。 append()将返回可能指向也可能不指向原始后备数组的新切片。

让我们看一个例子。让我们创建一个长度和容量为0的切片,并向其添加10个元素。为了查看何时发生新的重新分配,我们还打印了其第一个元素(第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])
}

这将输出:

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

如您所见,当我们附加第三个(i=2),第五个(i=4)和第九个元素(i=8时,以及当我们附加第三个元素时,都会分配一个新的支持数组追加了第一个元素(因为原始后备数组无法容纳任何元素)。

现在,当我们创建长度为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])
}

现在的输出将是:

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

如您所见,第一个元素的地址从未改变,这意味着在后台没有新的后备数组分配发生。

Go Playground上尝试该示例。

答案 1 :(得分:2)

要增加切片的容量,必须创建一个更大的新切片并将原始切片的内容复制到其中。

  

如果可以超出Golang中一个切片的容量,为什么会有   容量参数排在首位?

假设我们提前知道了slice的容量,那么我们可以使用make内置函数的Capacity参数来分配内存,而不是使用append来动态增加slice的容量,而这并不高效。

所以在下面的示例中

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

首先构建具有容量的切片所需的时间少于动态构建所需的时间

所以要回答您的问题,首先要考虑容量参数以提高性能和内存效率

答案 2 :(得分:1)

golang博客中有许多出色的文章。 one on slices,将为您提供切片的基础实现以及容量如何工作的详细见解。

该帖子介绍了append操作的工作方式以及容量值如何使append知道是重用基础内存阵列,还是在需要更多容量时分配更大的阵列。