Go十大常见错误第6篇(slice初始化常犯的错误)
前言
这是Go十大常见错误系列的第6篇:slice初始化常犯的错误。素材来源于Go布道者,现Docker公司资深工程师Teiva Harsanyi。
本文涉及的源代码全部开源在:Go十大常见错误源代码,欢迎大家关注公众号,及时获取本系列最新更新。
场景
假设我们知道要创建的slice的长度,你会怎么创建和初始化这个slice?
比如我们定义了一个结构体叫Bar
,现在要创建一个slice,里面的元素就是Bar
类型,而且该slice的长度是已知的。
方法1
有的人可能这么来做,先定义slice
var bars []Bar
bars := make([]Bar, 0)
每次要往
bars
这个slice插入元素的时候,通过append来操作bars = append(bars, barElement)
slice
实际上是一个结构体类型,包含3个字段,分别是- array: 是指针,指向一个数组,切片的数据实际都存储在这个数组里。
- len: 切片的长度。
- cap: 切片的容量,表示切片当前最多可以存储多少个元素,如果超过了现有容量会自动扩容。
type slice struct {
array unsafe.Pointer
lenint
capint
}type Pointer *ArbitraryType
如果按照上面的示例先创建一个长度为0的slice,那在append插入元素的过程中,
bars
这个slice会做自动扩容。如果
bars
的长度比较大,可能会发生多次扩容。每次扩容都要创建一个新的内存空间,然后把旧内存空间的数据拷贝过来,效率比较低。方法2
在定义slice的时候指定长度,代码示例如下:
func convert(foos []Foo) []Bar {
bars := make([]Bar, len(foos))
for i, foo := range foos {
bars[i] = fooToBar(foo)
}
return bars
}
这行代码
bars := make([]Bar, len(foos))
直接指定了slice的长度,无需扩容。方法3
在定义slice的时候提前指定容量,长度设置为0,代码示例如下:
func convert(foos []Foo) []Bar {
bars := make([]Bar, 0, len(foos))
for _, foo := range foos {
bars = append(bars, fooToBar(foo))
}
return bars
}
这种方法也可以,也无需扩容。
那方法2和方法3哪种好一点呢?其实各有优缺点:
- 从效率上来说,方法2比方法3要高一点,因为方法3里调用了append函数,再对bars赋值,效率比直接通过
bars[i] = fooToBar(foo)
要低一点。 - 从代码的可维护性来说,方法2不能通过append函数来插入元素,因为方法2里的slice定义的时候指定了长度,如果调用append,会扩容,往现有元素后面追加元素。方法3不能通过
bars[i] =
的方式来赋值,因为方法3里的slice定义的时候长度为0,如果使用bars[1]=
,会触发panic: runtime error: index out of range [1] with length 0
。
看完后你会彻底了解Go slice的原理和注意事项。
推荐阅读
- Go十大常见错误第1篇:未知枚举值
- Go十大常见错误第2篇:benchmark性能测试的坑
- Go十大常见错误第3篇:go指针的性能问题和内存逃逸
- Go十大常见错误第4篇:break操作的注意事项
- Go十大常见错误第5篇:slice初始化常犯的错误
- Go面试题系列,看看你会几题?
公众号:coding进阶。关注公众号可以获取最新Go面试题和技术栈。
个人网站:Jincheng's Blog。
知乎:无忌。
福利 我为大家整理了一份后端开发学习资料礼包,包含编程语言入门到进阶知识(Go、C++、Python)、后端开发技术栈、面试题等。
【Go十大常见错误第6篇(slice初始化常犯的错误)】关注公众号「coding进阶」,发送消息 backend 领取资料礼包,这份资料会不定期更新,加入我觉得有价值的资料。还可以发送消息「进群」,和同行一起交流学习,答疑解惑。
References
- https://itnext.io/the-top-10-...
推荐阅读
- 常见的java话题
- tidb|TiDB Lightning 错误处理功能
- 这10种记账方法是错误的,有些会计还在用!
- Apache错误代码:AH0058
- linux中证书错误Certificate|linux中证书错误Certificate verification failed
- SVN|SVN 出错信息汇总/错误信息一览表
- 和错误的人坦然分开
- 软件测试面试题|软件测试功能测试全套常见面试题【开放性思维题】面试总结4-3
- Django|Django 自定义404 500等错误页面的实现
- PyTorch|语义分割基础知识