slice的几种写法

点击↑上方↑蓝色“编了个程”关注我~

232a211a46e6fa11520aa95b80213b1a.png

这是Yasin的第 59 篇原创文章

35e6635564940fd84a2636d57653c7c5.png

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初始化其实是比较推荐的一种方式。需要传入lencap或者只传入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》。

969f9a38fdc5bbabd85734dc5bd1579c.png

小Y去google了一下,四年前真的有人去给Goland提了这个bug,但Goland的人并不认为它是一个bug。小Y大概看了一下,这个问题至今其实并没有解决,负责解决这个问题的人只是建议把这个警告关掉。链接在这:https://youtrack.jetbrains.com/issue/GO-5317,感兴趣的同学可以移步看看。

c98596bb7cf87b7250051238febf28d9.png

总结

总共有三种声明及初始化方法,不同的声明方式适用于不同的场景。小Y个人用第二种比较多,其实哪怕是空数组,声明一下也比nil不会多用太多的内存,但能防止json反序列化等异常,所以其实个人很少使用第一种方式。而第三种方式,也是测试的时候可能用得多一点。

c5f1ca0834b7d768db48e4dd70c3ec87.png

关于作者

我是Yasin,一个爱写博客的技术人

微信公众号:编了个程(blgcheng)

个人网站:https://yasinshaw.com

欢迎关注这个公众号68e797a90c80477bbe1df967904dc9c5.png

d584259d6342c2fde2448d57a992a64c.png

往期推荐

Golang之context

业务代码和技术代码

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值