How to use slice capacity and length in Go

hww_面条酱 · · 949 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

huangwenwei - 字里行间 | Golang 中的数组 (array) 和切片 (slice)

中文描述约定
array: 数组
slice: 切片

来做个测试,不在机器上运行下面的代码,请给出输出

vals :make([]int, 5)
for i:= 0; i < 5; i++ {
    vals = append(vals, i)
}
fmt.Println(vals)

如果你猜测的结果是 [0 0 0 0 0 1 2 3 4],恭喜你答对了。
为啥么正确答案不是 [0 1 2 3 4] 呢?
大部分像我一样 Golang 初学者都会打错这道测试题,在这篇文章中,我们来探讨一下为什么不是我们期望的输出结果以及 slice 数据类型中 capacity 和 length 的用法。

Slices VS Arrays

初学时可能会把 Golang 中数组和切片的区别,但是如果习惯了使用切片,将会极大地优化代码;
数组和切片有很多不同点,但在这里我们记住它们之间最大的区别就是:固定的数组大小是数组声明时的一部分,而切片可以有一个动态的大小。那么在实际的编码中是如何体现的呢?例如 val a [10]int, 这个数组有一个固定的大小 10 ,却不能改变长其度;如果我们调用 len(a),返回 10,因为长度就是数组的一部分;这就导致一个问题,如果有数组长度超过 10 的需求,你必须新建另外一个数组对象,譬如 val b [11]int, 并把数组 a 的值拷贝到数组 b
在某种场景下固定长度的数组非常使用,但是通常我们长度灵活的数组,需要一种“没有固定大小的数组”,最直接的做法是声明数组时给足够大的长度,例如:

var vals [20]int
for i := 0; i < 5; i++ {
    vals[i] = i * i
}
subsetLen := 5

fmt.Println("The subset of our array has a length of:" subsetLen)

// Add a new item to our array
vals[subsetLen] = 123
subsetLen++
fmt.Print("The subset of our array has a length of:", subsetLen)

上面的例子中,我们声明数组时设置了其长度为 20 ,通过一个变量我们“设置”其长度为 5 ,我们往其加了一个元素之后, subset 自增 1,数组长度就变成了 6
粗略地说,这就是切片的原理,通过设置一个值来限定数组的最大长度,在上述例子中为 20 ;同时切片还有一个值来表示数组元素的真实长度,在上述例子中为 subsetLen ;故切片中有 capacity cap 和 length len 两个概念。切片的内部实现基于数组,但是使用方式更灵活、运行更高效。cap 限定了切片中元素个数增加的上限,通过 append 函数,我们不需关心数组底层的个数上限。

回到我们的测试题

明白了数组和切片的概念和不同点之后,我们再次解释一下测试题。
make 函数能够声明一个切片,第一个参数表示类型,第二个参数表示类型的长度,第三个参数为可选参数,表示类型的容纳上限;
make([]int, 5) 表示我们要新建一个长度为 5 的切片,切片的默认容纳上限(capacity)和长度一致。但是 append 函数表示切片增加一个元素,那么该切片的容纳上限将同时增加,并在切片的末尾新增一个元素。也就是默认的切片值为 [0 0 0 0 0] 增加一个元素将会被放置在在第五个元素后面,所以运行结果如下:

vals := make([]int, 5)  
fmt.Println("Capacity was:", cap(vals))  
for i := 0; i < 5; i++ {
    vals = append(vals, i)
    fmt.Println("Capacity is now:", cap(vals))
}
fmt.Println(vals)
// output: [0 0 0 0 0 0 1 2 3 4]

如果修改为下面的代码,则输出为 [0, 1, 2, 3, 4]

vals := make([]int, 5)  
for i := 0; i < 5; i++ {  
      vals[i] = i
}
fmt.Println(vals)

代码不够简洁,改造一下:我们把 vals 的长度设置为 0 ,容纳上限为 5

vals := make([]int, 0, 5)
for i := 0; i < 5; i++ {
    vals.appen(i)
}
fmt.Println(vals)

最后,如果能知道切片的上限,建议最好给 make 函数加上第三个参数。


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:hww_面条酱

查看原文:How to use slice capacity and length in Go

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

949 次点击  
加入收藏 微博
上一篇:NSQ深入与实践
下一篇:docker快速入门
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传