golang中slice切片使用的误区

一、前言

      最近在项目代码中,发现了一种写法,如下所示:

//期望是能把id顺序写到切片,把切片占满,元素数为10个
Ids1 := make([]int64, 10)
for _, v := range xxxx {
    
    
   Ids1 = append(Ids1, v.ids1)
}

      看起来还可以,提前给切片分配长度,防止append频繁扩容。然而在实际使用中才发现,以上结果打印如下:

Ids1:([0 0 0 0 0 0 0 0 0 0 393 394 395 396 397 398 399 400 401 402])

      是不是跟想象中不一样,这特么的不是说好的分配长度,使用已有长度,怎么打印出来的是0,而且切片依然发生了扩容。

二、切片分配长度的正确用法

1、切片的初始化

      博主百思不得其解,于是去百度切片的用法,发现一般初始化切片如下:

make([]T, len)   //省略cap,len=cap
make([]T, len, cap) // 等价于 make([]T, cap)[:len]

使用make初始化,由于未赋值系统默认将元素值置为0,即:

数值类型数组:   默认值为0
字符串数组:     默认值为""
bool数组:      默认值为false

那么我们打印出来的0值,是否就是被make初始化零值的原因呢?

2、打印切片长度和容量

	Ids1 := make([]int64, 10)
	fmt.Println("Ids1切片长度:", len(Ids1))
	fmt.Println("Ids1切片容量:", cap(Ids1))
	fmt.Println("Ids1切片内容:", Ids1)
	Ids2 := make([]int64, 0, 10)
	fmt.Println("Ids2切片长度:", len(Ids2))
	fmt.Println("Ids2切片容量:", cap(Ids2))
	fmt.Println("Ids2切片内容:", Ids2)

结果如下:

Ids1切片长度: 10
Ids1切片容量: 10
Ids1切片内容: [0 0 0 0 0 0 0 0 0 0]
Ids2切片长度: 0
Ids2切片容量: 10
Ids2切片内容: []

      通过打印结果我们发现,第一种初始化的方式,同时定义了lencap,导致切片默认分配了零值,当我们使用append的时候,由于新元素超过了cap(10),因此就发生了扩容,导致写入的元素堆到了后面。

      第二种初始化的方式,我们定义了cap,但是len=0,这样就不会默认给元素赋零值,因为切片实际上是没有任何元素的。此时append就可以把元素加进去,填充切片。

3、正确的用法

(1)下标指定

Ids1 := make([]int64, 10)
for k, v := range xxxx {
    
    
   Ids1[k] =  v.ids1
}

(2) 使用另一种初始化方式

Ids1 := make([]int64, 10)
Ids2 := make([]int64, 0, 10)
for _, v := range xxxx {
    
    
   Ids1 = append(Ids1, v.ids1)
   Ids2 = append(Ids2, v.ids2)
}

//打印结果如下:
 
 Ids1:([0 0 0 0 0 0 0 0 0 0 393 394 395 396 397 398 399 400 401 402])
 Ids2:([183 184 184 185 185 186 187 188 189 190])

      看似简单的代码,实际上也暗藏玄机。博主一开始也被误导了,代码看似提前分配了容量,实际上没鸟用,扩容跑不了,而且前10个元素为0,这个时候万一靠下标获取元素,直接出bug没商量。

end

扫描二维码关注公众号,回复: 15372540 查看本文章

猜你喜欢

转载自blog.csdn.net/LJFPHP/article/details/125826204
今日推荐