点击↑上方↑蓝色“编了个程”关注我~
这是Yasin的第 59 篇原创文章
Y说
最近生活风平浪静。工作时间还是比较忙,业余时间写文章的频率不高,本来想把关于slice的都写完,但后来发现太多了,于是打算拆成三篇来写。
前段时间花了大量的时间在弄小程序,本来都发布成功了,但后来某一次小程序升级说涉及交友服务,需要改类目,而交友类目需要电信增值服务的证,这个证比较难搞,所以可能后面打算终止这个小程序了,过段时间可能会把代码开源吧。
几种初始化
某天,小Y在写代码时,发现自己的一个Golang里对slice的声明及初始化有很多种不同的写法。
大概是这几种:
// slice
var s []int
// cap可以省略,但如果传值,必须大于等于len
s := make([]int, len, cap)
s := make([]int, len)
s := []int{}
s := []int{1, 2, 3}
// 这里顺便说下array,以下为array
var a [len]int
a := [len]int{}
a := [2]int{0, 1}
小Y比较奇怪,这几种写法有什么区别呢?于是网上查询了一些资料,总结了一下。
只声明不初始化
首先是第一种写法,只声明不初始化。这种写法在其它语言中也很常见,比如Java中的:
A a;
在Golang中,slice其实本质上是一个指针。所以只声明不初始化时,它的值是nil
。基于这个规则,以下代码会有这个表现:
var s []int
// 输出 true
fmt.Println(s == nil)
// 输出 []
fmt.Println(s)
marshal, err := json.Marshal(s)
// 输出 null <nil>
fmt.Println(string(marshal), err)
这里需要注意的是json序列化一个nil
变量后,会输出字符串null
。反序列化时,也会反序列化为nil
。
这个有时候可能会有坑,比如给前端返回json的时候,本来约定了数组对象哪怕没值,也应该返回一个空列表[]
,这样前端好处理。但因为后端是这么写的,而后面的逻辑也因为去初始化,就会返回一个null
给前端。所以小Y个人不是很推荐用这种方式。
用make初始化
用make初始化其实是比较推荐的一种方式。需要传入len
和cap
或者只传入len
。slice底层是一个带有长度和当前位置指针的可扩容数组。其中cap代表整个数组当前的长度,扩容后会增加。而len代表的是有值的长度。
在make的时候输入len,会用默认值初始化,可以看以下代码:
s := make([]int, 2, 5)
// [0 0]
fmt.Println(s)
// 2
fmt.Println(len(s))
// 5
fmt.Println(cap(s))
什么情况下需要输出cap
呢?很明显,当你明确知道你的slice的长度时,推荐用这个,因为不用频繁扩容。关于扩容的东西后面会单写一篇文章。
->为什么这种情况不用array呢?因为array是值对象,每次传递都要copy,会有内存浪费,所以不推荐。
<-
这里也需要注意的是如果传了len
参数不为0,那后续使用append
方法,就是从len后面开始加值了。所以传了len一般是后续配合直接指定index赋值来使用的。
s := make([]int, 2, 5)
s = append(s, 1)
// [0 0 1]
fmt.Println(s)
字面量声明
字面量声明就是这种写法了,go会默认根据你声明的元素来设置好len和cap。
s := []int{}
// []
fmt.Println(s)
// 0
fmt.Println(len(s))
// 0
fmt.Println(cap(s))
s = []int{1, 2, 3}
// [1 2 3]
fmt.Println(s)
// 3
fmt.Println(len(s))
// 3
fmt.Println(cap(s))
这里第一行的写法,Goland编辑器也会给出warning提示:Empty slice declaration using a literal。它会推荐你改成上面第一种只声明,不初始化的写法。这两种写法都有其各自的优缺点,一个是空数组,一个是nil。知乎上有同学因为这个推荐遇到了前面提到的json序列化为null的问题,所以觉得这是一个Goland的bug,原文标题叫《Go 语言 IDE GoLand 的 BUG》。
小Y去google了一下,四年前真的有人去给Goland提了这个bug,但Goland的人并不认为它是一个bug。小Y大概看了一下,这个问题至今其实并没有解决,负责解决这个问题的人只是建议把这个警告关掉。链接在这:https://youtrack.jetbrains.com/issue/GO-5317,感兴趣的同学可以移步看看。
总结
总共有三种声明及初始化方法,不同的声明方式适用于不同的场景。小Y个人用第二种比较多,其实哪怕是空数组,声明一下也比nil不会多用太多的内存,但能防止json反序列化等异常,所以其实个人很少使用第一种方式。而第三种方式,也是测试的时候可能用得多一点。
关于作者
我是Yasin,一个爱写博客的技术人
微信公众号:编了个程(blgcheng)
个人网站:https://yasinshaw.com
欢迎关注这个公众号
往期推荐