Go语言中的slice,通俗的来说就是动态的数组;我们都知道,数组的长度是固定的,一旦声明后,数组的长度是固定的;但是slice的长度是不固定的,声明后,仍然可以继续增加slice的长度,我们先看一下slice的数据结构是怎样的。
typeslicestruct{arrayunsafe.Pointerlenintcapint}
如上所示:
array是数组的一个指针
len是当前切片的元素个数
cap表示当前切片的容量(即最大可以容纳多少个元素)
array指向的地方是一块连续的内存空间,存储切片的元素;连续的内存空间+长度+容量组成了切片,这里可以看成是数组的一个抽象,因为如果len和cap保持不变,slice本质就是一个数组;但slice和cap会发生变化,所以这就是切片。
切片的声明:
m:=[]int{1,2,3}m:=make([]int,3)
如果我们要获取切片的一部分,可以这样
m:=[]int{1,2,3}n0:=m[0:2]//输出[1,2]n1:=m[:2]//输出[1,2]
通过m[start:end]的方式获取切片的一部分,包含start不包含end,也可以不写start, m[:2]表示从开始到原切片下标为1的元素为止,不写end,m[1:]表示从下标为1的元素开始到原切片元素结束为止。\ \ 我们下面再看一道有意思的代码,再理解下slice:
funcmain(){m:=[]int{1,2,3}n:=m[:2]n=append(n,4)n=append(n,5)n=append(n,6)fmt.Printf("%v\n",m)n[0]=7fmt.Printf("%v",m)}
上面的代码会输出什么呢.一开始是不是会觉得第一个输出是:[1,2,4,5,6]\ 第二个输出是:[7,2,4,5,6]
正确答案肯定不是的,这里的正确输出两个都是:[1,2,4]
why?\ 我们一行行的来分析这段代码\
首先:m := []int{1, 2, 3},已经限定了m的len是3, cap是3。
n := m[:2],n变成[1,2], m依旧是[1,2,3],n引用了m的一部分,n的len是2,cap是3。
n = append(n, 4), n变成[1,2,4],m变成了[1,2,4],因为n,m指向的同一块连续空间,n修改了第三个元素,m也会跟着变化。
n = append(n, 5),n的容量是3,这个时候发现容量不够了,需要扩容,扩容两倍,cap变成6,扩容后开辟了一个新的连续内存空间,此时,n与m已经没有关系了,所以不管后面n如何再append元素或者改变元素,都与m没有关系了。
那么两个输出都是[1,2,4]。
这里我们了解到slice的扩容机制:当cap不够的时候,如果slice的元素个数小于1024个扩大两倍;如果元素大于1024个,扩大1.25倍,并且每次扩容后会变成一个新的切片。