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

问题描述 投票:0回答:3

这是一个非常直截了当的问题:

如果可以超过Golang中切片的容量,为什么首先会有容量参数?

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

go slice
3个回答
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上的例子。


2
投票

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

如果可以超过Golang中切片的容量,为什么首先会有容量参数?

假设我们提前知道切片的容量,那么我们可以使用make内置函数的容量参数来分配内存而不是使用追加来动态增加切片的容量而不是内存效率。

所以在下面的例子中

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

构建具有容量的切片的时间少于动态构建的切片

所以为了回答你的问题,容量参数是第一位的,以获得更好的性能和内存效率


1
投票

golang博客中有很多优秀的帖子。这个one on slices,将让您详细了解切片的基本实现以及容量的工作原理。

这篇文章介绍了append操作的工作原理以及容量值如何让append知道是否重用底层内存数组,或者在需要更多容量时分配更大的数组。

© www.soinside.com 2019 - 2024. All rights reserved.