PRF&PUB:20170126 How to use slice capacity and length in Go.md

@geekpi
This commit is contained in:
wxy 2017-06-02 01:01:24 +08:00
parent 1ade5a5689
commit 83d144b665

View File

@ -1,7 +1,5 @@
<header class="post-header" style="text-rendering: optimizeLegibility; font-family: &quot;Noto Serif&quot;, Georgia, Cambria, &quot;Times New Roman&quot;, Times, serif; font-size: 20px; text-align: start; background-color: rgb(255, 255, 255);">[在 Go 中如何使用切片的容量和长度][14]
============================================================</header>
<aside class="post-side" style="text-rendering: optimizeLegibility; position: fixed; top: 80px; left: 0px; width: 195px; padding-right: 5px; padding-left: 5px; text-align: right; z-index: 300; font-family: &quot;Noto Serif&quot;, Georgia, Cambria, &quot;Times New Roman&quot;, Times, serif; font-size: 20px;"></aside>
在 Go 中如何使用切片的容量和长度
============================================================
快速测试 - 下面的代码输出什么?
@ -19,15 +17,15 @@ _[在 Go Playground 运行一下][1]_
_等等什么_ 为什么不是 `[0 1 2 3 4]`?
如果你在测试中做错了,你也不用担心。这是在 Go 过渡的过程中相当常见的错误,在这篇文章中,我们将说明为什么输出不是你预期的,以及如何利用 Go 的细微差别来使你的代码更有效率。
如果你在测试中做错了,你也不用担心。这是在过渡到 Go 语言的过程中相当常见的错误,在这篇文章中,我们将说明为什么输出不是你预期的,以及如何利用 Go 的细微差别来使你的代码更有效率。
### 切片 vs 数组
在 Go 中同时有数组和切片。这可能令人困惑,但一旦你习惯了,你会喜欢上它。请相信我
在 Go 中同时有数组array和切片slice。这可能令人困惑,但一旦你习惯了,你会喜欢上它。请相信我
切片和数组之间存在许多差异,但我们要在本文中重点介绍的内容是数组的大小是其类型的一部分,而切片可以具有动态大小,因为它们是围绕数组的装。
切片和数组之间存在许多差异,但我们要在本文中重点介绍的内容是数组的大小是其类型的一部分,而切片可以具有动态大小,因为它们是围绕数组的装。
这在实践中意味着什么?那么假设我们有数组 `val a [10]int`。该数组具有固定大小,且无法更改。如果我们调用 “len(a)”,它总是返回 10,因为这个大小是类型的一部分。因此,如果你突然在数组中需要超过 10 个项,则必须创建一个完全不同类型的新对象,例如 `val b [11]int`,然后将所有值从 `a` 复制到 `b`
这在实践中意味着什么?那么假设我们有数组 `val a [10]int`。该数组具有固定大小,且无法更改。如果我们调用 `len(a)`,它总是返回 `10`,因为这个大小是类型的一部分。因此,如果你突然需要在数组中超过 10 个项,则必须创建一个完全不同类型的新对象,例如 `val b [11]int`,然后将所有值从 `a` 复制到 `b`
在特定情况下,含有集合大小的数组是有价值的,但一般而言,这不是开发人员想要的。相反,他们希望在 Go 中使用类似于数组的东西,但是随着时间的推移,它们能够随时增长。一个粗略的方式是创建一个比它需要大得多的数组,然后将数组的一个子集视为数组。下面的代码是个例子。
@ -48,13 +46,13 @@ fmt.Println("The subset of our array has a length of:", subsetLen)
_[在 Go Playground 中运行][2]_
在代码中,我们有一个长度为 20 的数组,但是由于我们只使用一个子集,代码中我们可以假定数组的长度是 5然后在我们向数组中添加一个新的项之后是 6。
在代码中,我们有一个长度为 `20` 的数组,但是由于我们只使用一个子集,代码中我们可以假定数组的长度是 `5`,然后在我们向数组中添加一个新的项之后是 `6`
这是(非常粗略地说)切片是如何工作的。它们包含一个具有设置大小的数组,就像我们前面的例子中的数组一样,它的大小为 20。
这是(非常粗略地说)切片是如何工作的。它们包含一个具有设置大小的数组,就像我们前面的例子中的数组一样,它的大小为 `20`
它们还跟踪程序中使用的数组的子集 - 这就是 `append` 属性,它类似于上一个例子中的 `subsetLen` 变量。
最后,一个 slice 还有一个 `capacity`类似于前面例子中我们的数组20的总长度。这是很有用的, 因为它会告诉你的子集在无法容纳切片数组之前可以增长的大小。当发生这种情况时,需要分配一个新的数组,但所有这些逻辑都隐藏在 `append` 函数的后面。
最后,一个切片还有一个 `capacity`,类似于前面例子中我们的数组的总长度`20`)。这是很有用的,因为它会告诉你的子集在无法容纳切片数组之前可以增长的大小。当发生这种情况时,需要分配一个新的数组,但所有这些逻辑都隐藏在 `append` 函数的后面。
简而言之,使用 `append` 函数组合切片给我们一个非常类似于数组的类型,但随着时间的推移,它可以处理更多的元素。
@ -94,11 +92,11 @@ for i := 0; i < 5; i++ {
fmt.Println(vals)
```
当调用 `make` 时,我们允许最多传入 3 个参数。第一个是我们分配的类型,第二个是类型的 `长度`,第三个是类型的 `容量`_这个参数是可选的_
当调用 `make` 时,我们允许最多传入 3 个参数。第一个是我们分配的类型,第二个是类型的“长度”,第三个是类型的“容量”_这个参数是可选的_
通过传递参数 `make([]int, 5)`,我们告诉程序我们要创建一个长度为 5 的切片,在这种情况下,默认的容量与长度相同 - 本例中是 5。
虽然这可能看起来像我们想要的那样,这里的重要区别是我们告诉我们的切片,我们要将 `长度``容量` 设置为 5假设你想要在初始的 5 个元素_之后_添加新的元素我们接着调用 `append` 函数,那么它会增加容量的大小,并且会在切片的最后添加新的元素。
虽然这可能看起来像我们想要的那样,这里的重要区别是我们告诉我们的切片,我们要将“长度”和“容量”设置为 5假设你想要在初始的 5 个元素_之后_添加新的元素我们接着调用 `append` 函数,那么它会增加容量的大小,并且会在切片的最后添加新的元素。
如果在代码中添加一条 `Println()` 语句,你可以看到容量的变化。
@ -119,7 +117,7 @@ _[在 Go Playground 中运行][4]_
如何修复它呢?好的,这有几种方法,我们将讲解两种,你可以选取任何一种在你的场景中最有用的方法。
### 直接使用索引写入而不是 `append`
#### 直接使用索引写入而不是 `append`
第一种修复是保留 `make` 调用不变,并且显式地使用索引来设置每个元素。这样,我们就得到如下的代码:
@ -164,13 +162,13 @@ _[在 Go Playground 中运行][6]_
这样做很好,因为我们知道我们返回的切片的长度将与 map 的长度相同,因此我们可以用该长度初始化我们的切片,然后将每个元素分配到适当的索引中。这种方法的缺点是我们必须跟踪 `i`,以便了解每个索引要设置的值。
这就让我们引出了第二种方法。。。
这就让我们引出了第二种方法……
### 使用 `0` 作为你的长度并指定容量
#### 使用 `0` 作为你的长度并指定容量
与其跟踪我们要添加的值的索引,我们可以更新我们的 `make` 调用,并在切片类型之后提供两个参数。第一个,我们的新切片的长度将被设置为 `0`,因为我们还没有添加任何新的元素到切片中。第二个,我们新切片的容量将被设置为 map 参数的长度,因为我们知道我们的切片最终会添加许多字符串。
这会如前面的例子那样仍旧会在背后构建相同的数组,但是现在当我们调用 `append`它会将它们放在切片开始处因为切片的长度是0。
这会如前面的例子那样仍旧会在背后构建相同的数组,但是现在当我们调用 `append` 时,它会将它们放在切片开始处,因为切片的长度是 0。
```
package main
@ -197,11 +195,11 @@ _[在 Go Playground 中运行][7]_
### 如果 `append` 处理它,为什么我们还要担心容量呢?
接下来你可能会问:“如果 `append` 函数可以为我增加切片的容量,那我们为什么要告诉程序容量呢?
接下来你可能会问:“如果 `append` 函数可以为我增加切片的容量,那我们为什么要告诉程序容量呢?
事实是,在大多数情况下,你不必担心这太多。如果它使你的代码变得更复杂,只需用 `var vals []int` 初始化你的切片,然后让 `append` 函数处理接下来的事。
但这种情况是不同的。它并不是声明容量困难的例子,实际上这很容易确定我们的切片的最后容量,因为我们知道它将直接映射到提供的 map 中。因此,当我们初始化它时,我们可以声明切片的容量,并免于让我们程序执行不必要的内存分配。
但这种情况是不同的。它并不是声明容量困难的例子,实际上这很容易确定我们的切片的最后容量,因为我们知道它将直接映射到提供的 map 中。因此,当我们初始化它时,我们可以声明切片的容量,并免于让我们程序执行不必要的内存分配。
如果要查看额外的内存分配情况,请在 Go Playground 上运行以下代码。每次增加容量,程序都需要做一次内存分配。
@ -233,7 +231,7 @@ func keys(m map[string]struct{}) []string {
_[在 Go Playground 中运行][8]_
现在将此与相同的代码进行比较, 但具有预定义的容量。
现在将此与相同的代码进行比较但具有预定义的容量。
```
package main
@ -269,7 +267,7 @@ _[在 Go Playground 中运行][9]_
### 不要过分优化
如前所述,我通常不鼓励任何人担心这样的小优化,但如果最后大小的效果真的很明显,那么我强烈建议你尝试为切片设置适当的容量或长度。
如前所述,我通常不鼓励任何人这样的小优化,但如果最后大小的效果真的很明显,那么我强烈建议你尝试为切片设置适当的容量或长度。
这不仅有助于提高程序的性能,还可以通过明确说明输入的大小和输出的大小之间的关系来帮助澄清你的代码。
@ -293,7 +291,8 @@ _[在 Go Playground 中运行][9]_
作者简介:
Jon 是一名软件顾问,也是 “Web Development with Go” 一书的作者。在此之前,他创立了 EasyPost一家 Y Combinator 支持的创业公司,并在 Google 工作。
Jon 是一名软件顾问,也是 《Web Development with Go》 一书的作者。在此之前,他创立了 EasyPost一家 Y Combinator 支持的创业公司,并在 Google 工作。
https://www.usegolang.com
--------------------------------------------------------------------------------
@ -303,7 +302,7 @@ via: https://www.calhoun.io/how-to-use-slice-capacity-and-length-in-go
作者:[Jon Calhoun][a]
译者:[geekpi](https://github.com/geekpi)
校对:[校对者ID](https://github.com/校对者ID)
校对:[wxy](https://github.com/wxy)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出