聊聊 Golang 中的切片和数组

2023-06-12 14:44:56 浏览数 (2)

字数:1467, leoay 技术圈

你好, 我是 leoay, 又好几天不见了,今天我想聊一下 Golang 中切片和数组的区别。

说到数组,我们应该都不陌生吧,因为基本上每种编程语言中有它的身影;而切片呢?也是一种数据结构,python中也有切片的概念。

数组和切片都可以用来存储一组数据。

但是不同的是数组的长度是固定的,而切片则是可变的;切片就类似于一个可变的数组。

其实,在Go语言中数组和切片外表看起来很像,也因此有时候我们很容易搞混淆,下面我就用几个例子对比一下数组和切片的差异。

数组

因为数组是固定长度的,所以在定义数组时,我们必须指定数组的长度

代码语言:javascript复制
var array1 [3] int   //长度为3的整型数组,默认值是3个0

arr := [5]int{1, 2, 3, 4, 5}   //长度为5的数组,并赋值

var array2 = [...]int{6, 7, 8}  //不声明长度,长度取总元素个数3

array3 := [...] int {1,2,3} //不声明长度的数组,长度取总元素个数3

array4 := [...] int {99:-1}  //长度为100的数组,只有最后一个是-1,其他都是0(大括号中前面一个是最大下标,后面则是对应的值)

和其他语言的数组一样, Golang 的数组也是通过下标访问元素的。

slice

slice,即切片,表示一个拥有相同类型元素的可变长度序列。

slice通常被写为[]T,其中元素的类型都是T;它看上去就像没有长度的数组类型。

数组和slice其实是紧密关联的,它们可以很方便地相互转换。

slice可以看成是一种轻量级的数据结构,可以用来访问数组的部分或者全部元素,而这个数组称之为slice的底层数组。

但是slice和数组是不同的,slice有三个属性:指针,长度和容量,而数组就没有容量这个属性。

其中,指针指向底层数组的第一个可以从slice中访问的元素,这个元素不一定是数组的第一个元素。

长度指的是slice中的元素个数,不能超过slice的容量。

容量的大小通常大于等于长度,会随着元素个数增多而动态变化。Go语言的内置函数len 和 cap 用来返回slice的长度和容量。

下面看一下怎么定义slice:

代码语言:javascript复制
sllice1 := []int{1, 2, 3}  //注意与数组初始化的区别,在内存中构建一个包括有3个元素的数组,然后将这个数组的应用赋值给s这个Slice

array1 := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}  //a是数组
slice2 := array1[2:8]    //从数组中切片构建Slice

slice3 := make([]int, 10, 20)  //make函数初始化,len=10,cap=20

从上面的例子中我们可以看出,数组和slice还是很像,我们可以通过数组构建slice,也可以直接用make创建空的slice,至于make的用法,其实在 Go 中有好几种,后面 涉及到 channel 的文章时再细说。

关于 slice 的扩容

我们已经知道了 slice 的长度不是固定,所以它支持扩容。

在 Go 中我们也用 append 函数来为 slice 扩容。但是这儿有个疑问,如果我们在定义 slice 时已经指定好了长度和容量, 那么扩容时会有什么影响呢?

一般来说,如果我们在对 slice 追加元素时, 容量不够了, 那么其容量一般按照原来的2倍进行扩容, 而长度呢,则会更新为实际的元素个数,可以通过下面这部分代码看效果:

代码语言:javascript复制
package main 

import "fmt"

func print_info(my_slice []int) {
 fmt.Println("len :",len(my_slice))
 fmt.Println("cap :",cap(my_slice))
 for i,v :=range my_slice{
  fmt.Println("element[",i,"]=",v)
 }
 fmt.Printf("%p", my_slice)
}

func main() {
 slice1 := make([]int,5,6)
 print_info(slice1)
 slice2 := append(slice1,8,9,10)
 print_info(slice02)
}

其实,进一步观察上面的代码,当我们把扩充前后的 slice 的地址打印出来后发现,它们是不一样的,这就说明扩充并不只是简简单单地在原来的 slice 后面追加元素,而是新创建了一个 slice。

实际上新的 slice 中的前面的元素是从原来的slice中拷贝过来的。

好了,今天的这篇文章就写到这里了,怎么样?看完以后是不是觉得对 Go 中数组和 slice 的认识又多了亿点点,如果觉得文章写得 ok,请给个点赞,以后我会花更多时间陪你在技术的海洋中遨游!

0 人点赞