为了大茶缸的我,在陵城的一点半,又一次打开电脑,坐了下来,开始了这篇文章的一个爆肝,虽然说还是一篇相对基础的文章类型,但也算是对于我个人在学习Go的道路上的一个记录吧。
前言
老规矩,先简单介绍一下切片,其实切片在Go里面,它是一种对于数组的一种视图,打个比方的话,数组就是一块地,视图就是一个窗口,透过这个窗口,可以看到这个数组里面的内容。
切片的基础概念
要声明一个切片的方式,跟数组非常的像,唯一的不同,就是中括号内没有任何东西。
a := []int {1,2,3,4,5} 直接创建
b := [3]int {1,2,3}
c := b[:] 通过从数组里面去切
d := c[0:1] 通过从切片里面去切
切片的索引
从上面的例子,我们可以看到,我在创建切片的时候用到了[:] 这样的一个东西,这里面的两个就是切片的索引,冒号左边为起始位置,右边为结束位置。
它的使用上非常的自由,但是需要注意的一点是,切片的索引不可以为负数。
切片的威力
切片其实是一种对于一个数组的快照,也就是它的底层其实依然是指向一个数组的,也就是说,如果我们对这个切片进行了修改,同样的也会对底层的数组造成改动,而同样是切的这个数组的切片也会被改变,这里举个例子
b := [3]int {1,2,3}
c := b[:]
g := b[:]
c[0] = 10
fmt.Print(b,g)// [10 2 3] [10 2 3]
这里我们可以看到,虽然我们只是改变了c切片,但是本身的数组b和另一个切片g也跟着做出了改变,这就是因为改切片其实是b数组的一个快照,其实本质上它底层还是b数组的。
切片也可以排序
切片也是可以排序的,因为在标准库里面有一个sort的包,而这个包里面有很多对于不同数组类型的排序方法,就可以满足我们的需求
a := []int {3,2,4,1}
sort.Ints(a) //对a进行了排序
因为排序这个比较重要,我就单独拿出来收一下,另外标准库里面还有很多其他对切片的一些方法,在这里我就不一一列举了。
更大的切片
我们知道,数组在一开始设置了它的大小之后,是没有办法再进行扩容的了,但是切片却可以,但在介绍扩容的方法前,要先了解切片的长度和容量这两个概念。
长度和容量
在切片中我们通过len看到的长度并不是切片真正的长度,我们知道,切片的底层是数组,这里我找了一张图来说明
我们可以看到,切片内部一共有三个类型,一个是切片的内容,一个是长度len,还有一个容量cap,其中长度代表的是切片本身所见的长度大小,而容量则代表这该切片底层的数组的长度。
make一个切片
在申请一个切片的时候,还有另外一个方法是可以通过Make来完成,它主要会接触三个参数,一个是申请的切片的类型,一个是切片长度,一个是切片容量。
a := make([]int, 0, 3) // 声明一个长度为0,容量为3的切片
ints := append(a, 2, 2, 2)
fmt.Print(ints) // [2 2 2]
使用make创建变量的好处
当我们使用make来创建变量的时候,其实就是实行了一个预分配的动作,它为底层的数据库设置了一个初始的容量,这样就可以避免额外的内存分分配和数据复制的这些动作了。
深入理解append
在切片中,如果想要对该切片进行扩充,可以使用的方法就是append。
a := []int {1,2,3,4}
ints := append(a, 5)
fmt.Print(ints) // [1 2 3 4 5]
如果想要将两个切片合并的话,也非常的简单,这里会用到字符子面量的另一个用法,它用起来跟javascrip的解构非常的类似,只是摆放的位置不一样
a := []int {1,2,3,4}
b := []int {5,6,7}
ints := append(a, b...)
fmt.Print(ints) // [1 2 3 4 5 6 7]
那么,问题来了,当对一个切片扩容的时候,如果已经超过了这个切片本身的容量的时候,怎么办嘞? 这里我们做一个实验,相信大家就懂了
b := make([]int,3,5) // 申请一个长度为3,容量为5的切片
ints := append(b, 2,2)
i := append(b, 2, 3, 4, 5)
fmt.Printf("b:%p,ints:%p,i:%p",b,ints,i)
//b:0xc0000181b0, ints:0xc0000181b0, i:0xc0000200a0
通过上面的那个例子,我们可以看到,ints和初始的b所指向的数组的内存位置是同一个位置,当我们给切片插入的数据量大于它的容量的时候,其实它本质上是底层重新给这个切片分配了一个新的更大的一个数组结构,也就是会指向一个新的内存位置。
因为它超容的时候,是指向了一个新的数组,所以当我们对这个切片进行修改的时候,也就不会影响到老的那个切片了。
三索引的切分
通过上面我们知道,切片不止有长度,还有容量,那么在切分切片的时候,同样的也可以对容量也进行一个切分。
a := []int {1,2,3,4,5}
b := a[0:2:4]
fmt.Print(b,cap(b)) //[1 2] 4
这样就不只是对视图层的一个切片,同样也剋以把容量也给切了。
至于什么时候要用到三索引,这个就根据你的业务来看了。
最后
切片可以说是我接触Go里面,基础知识中让我惊艳的地方,而它的使用上也同样的美妙,它不像javascript这样的脚本语言过于的自由,又不会跟Java那样的严谨,总之Go的美好,后面的时候,让我们在满满体会吧。