来做一个快速测验-以下代码输出什么?
Run it on the Go Playground → https://play.golang.org/p/7PgUqBdZ6Z
[0 0 0 0 0 0 1 2 3 4][0 1 2 3 4]
如果答错了,也不担心。从其他语言过渡到Go时,这是一个相当普遍的错误,在本文中,我们将介绍为什么输出不符合你的预期以及如何利用Go的细微差别来提高代码效率。
Slices vs Arrays
var a [10]intlen(a)[10]int
虽然在特定情况下使用具有固定大小的数组很有价值,但通常来说这并不是开发人员想要的。相反,我们希望使用与Go中的数组类似的东西,但是具有随着时间增加长度的能力。一种简单的方法是创建一个比需要的数组大得多的数组,然后将该数组的子集当作使用的数组。下面的代码显示了一个示例。
Run it on the Go Playground → https://play.golang.org/p/Np6-NEohm2
上面代码中,我们将一个数组其大小设置为20,但是由于我们仅使用一个子集,因此我们的代码可以假装数组的长度为5,然后在向数组中添加新项后为6。
lengthsubsetLen
append
sliceappend
让我们再次看一下前面的示例,但是这次我们将使用切片而不是数组。
Run it on the Go Playground →https://play.golang.org/p/M_qaNGVbC-
appendlencap
考虑到这一点,让我们回顾一下文章开头的测验代码,看看出了什么问题。
make
[0 ,0 ,0 ,0 ,0]append
Println()
Run it on the Go Playground →https://play.golang.org/p/d6OUulTYM7
[0 0 0 0 0 0 0 1 2 3 4][0 1 2 3 4]
不使用 append, 直接用索引写入
第一个解决方法是保持make调用不变,并明确声明要将每个元素设置为的索引。
Run it on the Go Playground → https://play.golang.org/p/d6OUulTYM7
我们设置的值恰好与我们要使用的索引相同,但是您也可以独立跟踪索引。 例如,如果您想获取map的key,则可以使用以下代码:
Run it on the Go Playground → https://play.golang.org/p/kIKxkdX35B
i
这导致我们进入第二种方法
使用0作为长度,并指定容量
我们更新make调用,在切片类型之后为其提供两个参数。首先,新切片的长度将设置为0,因此我们没有在切片中添加任何新元素。第二个参数是新切片的容量,将被设置为map参数的长度,因为我们知道切片最终的长度就是 map 的长度。
append
Run it on the Go Playground →https://play.golang.org/p/h5hVAHmqJm
使用 append 能自动扩容,为什么还要关心切片的容量
你可能要问的下一件事是:“如果append函数可以为我增加切片的容量,我们为什么还要告诉程序一个容量?”
var vals []intappend
请在Go Playground上运行以下代码。每当容量增加时,我们的程序就需要执行另一次内存分配:
Run it on the Go Playground → https://play.golang.org/p/fDbAxtAjLF
现在将切片预设容量后将其与上面相同的代码进行比较:
Run it on the Go Playground → https://play.golang.org/p/nwT8X9-7eQ
slicekeys()
不要过度优化
通常不鼓励任何人担心像这样的次要优化,但是在确实很明显最终大小应该是多少的情况下,强烈建议为切片设置适当的容量或长度。
它不仅有助于提高应用程序的性能,而且还可以通过明确说明输入大小和输出大小之间的关系来帮助理清代码。
本文并不是要对切片或数组之间的差异进行详尽的讨论,而只是要简要介绍容量和长度如何影响切片以及它们在不同解决方案中的作用。