【GoLang那点事】Go数组和切片你不知道的区别

#### 开篇语 > 数组和切片是两种不同的数据结构,比较常见,在Go语言中同时存在,今天我们就一起来看看他们在使用方式上,原理上的一些区别? #### 数组 * 在Go语言中,数组是一种具有相同类型固定大小的一种数据结构。 > 我们先来看看数组的使用,数组类型声明时的方式是 []T ,前面的[]指定数组的大小,T指定数组的类型,如下我们声明了一下数组,数组的大小是3,在没指定数组初始值时数组默认初始值是{0,0,0} ```go array1 := [3]int{} //我们可以通过如下方式给数组赋值 array1[0] = 1 array1[1] = 2 array1[2] = 3 //下面这种也是数组声明的一种方式,并且初始化数组的值为{1,2} array2 := [2]int {1,2} ``` > 思考一下前面我们array1赋值给array2对吗?记住,这种方式是错误的,在Go语言中只有**大小相等**,类型相同的数组才是同类型的数组,之间才可以相互赋值,如下截图,我们可以看见array1赋值给array2时编译器报错 ```go array2 = array1 ``` ![](https://app.yinxiang.com/shard/s65/res/b27b8690-8676-4b19-a43a-f4e3f7f7e132/Image.png) > 看上面的图,我们再思考一个问题,我们把array3 赋值给array2后,修改了array3下标为0的值等于6,请问打印的结果是?这个问题考验的是我们把array3赋值给array2后,修改了array3的值,会对array2产生影响吗?答案是不会,记住,在Go中,数组属于基本类型,他们之间的赋值,传递是属于值拷贝,同样的,如果将数组作为参数在函数间传递,也是属于值拷贝 ``` 第一种结果:0,0,0;  4,5; 6,5 第二种结果:0,0,0;  6,5; 6,5 ``` * 数组的长度,我们声明了一个数组,那如何获取数组的长度呢?通过`len(array4)`获取数组的长度 ```go array4 := [2]int {1,2} l := len(array4) fmt.Println(""l) ``` * 多维数组的声明,多为数组可以想象成就是多个一维数组,如下声明了一个二维数组,代表有两个一维数组,每个一维数组的长度是2 ```go array4 := [2][2]int{} array6 := [2][2]int{{1,2},{3,4}} ``` * 对数组的访问和遍历 ```go //访问数组中的元素 array4 := [2]int {1,2} //访问数组下标为0处的值并打印 fmt.Println(array4[0]) //通过range遍历数组, //i代表数组的下标, //v代表数组下标为i处的值 for i,v := range array4{     fmt.Println(i,v) }     array5 := [2][2]int{{1,2},{3,4}} for i,tempArray := range array5{     //此时tempArray是一维数组     //再通过range 遍历tempArrayy一维数组     for j,v := range tempArray{         fmt.Println(j,v)     } } ``` #### 切片 > 在Go语言中,切片是数组的一种高级运用,相对于数组,切片是一种更加方便,灵活,高效的数据结构。,切片并不存储任何元素而只是对现有数组的引用(不是值拷贝,是指针) > **切片的声明方式有以下几种** * 通过数组创建一个切片 ```go array1 := [3]int{1,2,3} //将数组下标从1处到下标2处的元素转换为一个切片(前闭后开) slice1 := array1[1:2] ``` * 直接声明一个切片 ```go //下面代码直接出初始化一个切片 (这里大家有个疑问,我不管怎么看都觉得它是一个数组啊) //记住,再go语言中,区别一个变量是数组还是切片,就看有没有定义长度 //有定义长度就是数组,如array1,没定义就是切片 如slice2 //我们也通过fmt.Println(reflect.TypeOf(array1),reflect.TypeOf(slice2)) //上面这代码打印的结果是[3]int,[]int,可以看到前者有长度,后者没有 slice2 := []int{1,2,3} ``` * 通过make函数创建一个切片,也是最常用的 ```go //[]int,指定切片的类型,3是切片的长度,6是切片的容量 slice3 := make([]int,3,6) ``` * 通过切片生成一个切片 ```go //声明一个切片 slice4 := []int {1,2,3,4,5,6} //通过slice4创建一个切片,元素是slice4下标从0到1(不包含1)的元素 slice5 := slice4[0:1] //通过slice4创建一个切片,元素是slice4下标从0到末尾的元素 slice6 := slice4[1:] //通过slice4创建一个切片,元素是slice4下标从0到3的元素 slice7 := slice4[:3] ``` > 上面我们介绍了切片的几种常见构造方式,接下来我们看看如何操作切片 ```go slice3   := make([]int,3,6) //给切片赋值 slice3[0] = 0 slice3[1] = 1 slice3[2] = 2 //通过len([]Type) cap([]Type)两个函数查看切片的长度和容量 fmt.Println(len(slice3),cap(slice3)) 结果:3,6 ``` > 思考一下我们能给上面切片slice3下标为3处赋值吗 ? `slice3[3] = 3`,答案是不能的,虽然我们定义了切片的长度是3,容量是6,但对切片的操作是以长度为准的,如果已经赋值到最大长度了,怎么办呢?切片为我们提供了`append([]Type, elems...Type)[]Type` 方法向切片中追加元素 []Type代表传入一个切片,elems代表追加的元素,可以传多个。 ```go //想slice3 切片追加三个元素,返回一个新的切片 slice3 = append(slice3,3,4,5) //此时再次查看切片的长度和容量 fmt.Println(len(slice3),cap(slice3)) 结果: 6, 6 切片的长度和容量保持一致了 //思考一下,我们再次append能给切片追加元素吗? 肯定可以的,前面说过切片是可扩长的 slice3 = append(slice3,7,8,9) //此时再次查看切片的长度和容量 fmt.Println(len(slice3),cap(slice3)) 结果:9, 12 发现了什么?在切片容量满时,切片的扩容时翻倍的,也就是新的切片的容量时原切片的容量的2倍 ``` > 知道了切片的追加,长度,容量,那么如何删除切片里的元素呢?如果你在看通过切片创建一个切片时思考过,你就知道如何删除切片中的元素了 ```go //我们创建了一个切片,有四个元素,下标命名为0,1,2,3 slice11 := []int{1,2,3,4}  //假设我们要删除下标为0的元素,这段代码的含义是 创建一个空的切片 slice11[:0] 创建一个从下标1到末尾元素的切片 slice11[1:] 给空切片添加元素并返回一个新的切片 slice12 := append(slice11[:0],slice11[1:]...) //同上我们得出删除下标为i的元素的切片的公式时 sliceTemp := append(slice11[:i],slice[i+1:]...) ``` > 下图是切片删除的一个过程 ![](https://app.yinxiang.com/shard/s65/res/1eff0513-bb1c-41f9-b296-effb0379e19b/Image.png) #### 数组和切片的底层存储 > 我们看下图分析 ![](https://app.yinxiang.com/shard/s65/res/6a92ad13-2fb0-4299-85b9-4e6023bdd851/Image.png) > 基于上图我们会发现,切片的底层就是数组,切片是通过指针的形式指向不同数组的位置从而形成不同的切片,切片对本身元素的修改,也会影响到数组和其它的切片。看下面代码,大家猜一猜输出结果 ```go array11 := [5]int{1,2,3,4,5} slice11 := array11[0:3] slice11[0] = 10 sliceTemp := append(array11[:2],array11[3:]...) slice11[0] = 11 fmt.Println(array11,slice11,sliceTemp) //输出结果:[11 2 4 5 5] [11 2 4] [11 2 4 5] ``` **欢迎大家关注微信公众号:“golang那点事”,更多精彩期待你的到来**

0 个评论

要回复文章请先登录注册