从零学 Go:切片中的应用实践

举报
aoho 发表于 2021/09/26 19:09:07 2021/09/26
【摘要】 前文回顾前面的文章主要介绍了 Go 语言中,切片的基本概念以及使用。本文将会继续介绍切片的相关应用于相关的函数。 切片除了前面一篇文章介绍的切片初始化方式。还可以直接声明新的切片,这就类似于数组的初始化,但是不需要指定其大小,否则就变成了数组,样式如下所示:var name []T此时声明的切片并没有分配内存,我们可以在声明切片的同时对其进行初始化,如下例子所示:ex := []int{1...

前文回顾

前面的文章主要介绍了 Go 语言中,切片的基本概念以及使用。本文将会继续介绍切片的相关应用于相关的函数。

切片

除了前面一篇文章介绍的切片初始化方式。还可以直接声明新的切片,这就类似于数组的初始化,但是不需要指定其大小,否则就变成了数组,样式如下所示:

var name []T

此时声明的切片并没有分配内存,我们可以在声明切片的同时对其进行初始化,如下例子所示:

ex := []int{1,2,3}
fmt.Printf("ex value is %v\n", ex)
fmt.Printf("ex len is %v\n", len(ex))
fmt.Printf("ex len is %v\n", cap(ex))

结果如下所示:

ex value is [1 2 3]
ex len is 3
ex cap is 3

此时声明的切片大小和容量都为 3。

Golang 中提供了 append 内建函数用于动态向切片添加成员,它将返回新的切片。如果当前切片的容量可以容纳更多的成员,添加的操作将在切片指向的原有数组上进行,这将会覆盖掉原有数组的值;如果当前切片的容量不足以容纳更多的成员,那么切片将会进行扩容,具体的扩容过程为:申请一个新的连续内存空间,空间大小一般是原有容量的 2 倍,然后将原来数组中的数据拷贝到新的数组中,同时将切片中的指针指向新的数组,最后将新的成员添加到新的数组中。我们将通过下面的例子来进行演示:

package main

import "fmt"

func main()  {


	arr1 := [...]int{1,2,3,4}
	arr2 := [...]int{1,2,3,4}

	sli1 := arr1[0:2] // 长度为 2,容量为 4
	sli2 := arr2[2:4] // 长度为 2,容量为 2

	fmt.Printf("sli1 pointer is %p, len is %v, cap is %v, value is %v\n", &sli1, len(sli1), cap(sli1), sli1)
	fmt.Printf("sli2 pointer is %p, len is %v, cap is %v, value is %v\n", &sli2, len(sli2), cap(sli2), sli2)

	newSli1 := append(sli1, 5)
	fmt.Printf("newSli1 pointer is %p, len is %v, cap is %v, value is %v\n", &newSli1, len(newSli1), cap(newSli1), newSli1)
	fmt.Printf("source arr1 become %v\n", arr1)

	newSli2 := append(sli2, 5)
	fmt.Printf("newSli2 pointer is %p, len is %v, cap is %v, value is %v\n", &newSli2, len(newSli2), cap(newSli2), newSli2)
	fmt.Printf("source arr2 become %v\n", arr2)

}

上述例子的结果为:

sli1 pointer is 0xc00000c040, len is 2, cap is 4, value is [1 2]
sli2 pointer is 0xc00000c060, len is 2, cap is 2, value is [3 4]
newSli1 pointer is 0xc00007c020, len is 3, cap is 4, value is [1 2 5]
source arr1 become [1 2 5 4]
newSli2 pointer is 0xc00007c060, len is 3, cap is 4, value is [3 4 5]
source arr2 become [1 2 3 4]

通过上面的例子,我们可以发现,容量足够的 sli1 直接将 append 添加的新成员覆盖到原有数组 arr1,而容量不够的 sli2 进行了扩容操作,申请了新的底层数组,不在原数组的基础上进行操作。在实际使用的过程中要千万记住这两种区别。

如果原有数组可以添加新的成员,即切片指向的数组后还有空间,但切片的容量已经饱和,此时进行 append 操作,同样会进行扩容,申请新的内存空间,如下例子所示:

arr3 := [...]int{1,2,3,4}
sli3 := arr3[0:2:2] // 长度为 2,容量为 2

fmt.Printf("sli3 pointer is %p, len is %v, cap is %v, value is %v\n", &sli3, len(sli3), cap(sli3), sli3)

newSli3 := append(sli3,5)
fmt.Printf("newSli3 pointer is %p, len is %v, cap is %v, value is %v\n", &newSli3, len(newSli3), cap(newSli3), newSli3)
fmt.Printf("source arr3 become %v\n", arr3)

对应的输出结果为:

sli3 pointer is 0xc00008e080, len is 2, cap is 2, value is [1 2]
newSli3 pointer is 0xc00006e0a0, len is 3, cap is 4, value is [1 2 5]
source arr3 become [1 2 3 4]

在上述代码中,我们指定了创建切片的第三个参数 capcap 必须大于 end,生成切片的容量将为 cap - begin,限定了 sli3 的容量为 2。当进行 append 操作时,即使原有数组存在足够的空间,newSli3 还是指向新的数组空间。

为了方便切片的数据快速复制到另一个切片中,Golang 提供了内建的 copy 函数,它的使用样式如下:

copy(destSli, srcSli []T)

它的返回结果为实际发生复制的元素个数。如果要保证来源切片的数据都拷贝到目标切片,需要保证目标切片的长度大于等于来源切片的长度,否则将按照目标切面的长度大小进行拷贝。

小结

总得来说 Go 语言中数组和切片有如下的区别:

  • 切片是指针类型,数组是值类型
  • 数组的长度是固定的,而切片不是(切片是动态的数组)
  • 切片比数组多一个属性:容量(cap)
  • 切片的底层是数组

本文主要介绍了切片的一些函数,切片是 Go 中提供了一种灵活,功能强悍的内置类型(“动态数组”)。与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。