Go语言中的slice
1.Go语言中slice的组成
Go语言中的切片由三部分组成,第一部分是切片元素的存储位置,第二部分是切片中元素的数量,第三部分是切片的容量。
2.slice的创建
声明一个整型切片ints如下图所示:
var ints []int
变量Ints由三部分组成:
切片元素要存在一段连续的内存中,实际上与数组相同,data就是该切片对应底层数组的起始地址,目前只分配了切片结构,还没有分配底层数组,因此这里的data值为nil,存储元素个数和容量都为0。如果使用make来创建切片,如下所示:
var ints []int = make([]int,2,5)
系统就会开辟一段容纳5个元素的内存作为ints的底层数组,并将该数组中的每一个元素初始化为0。
由于make创建的切片中只包含两个元素,因此当前的Ints为:
3.切片的使用
可以使用append方法向切片中添加元素。
ints = append(ints,1)
添加后切片变为:
包含的元素个数(length)修改为3,并在底层数组中添加对应的元素。
已经存储的元素可以安全读写,比如我们可以使用
ints[0] = 1
来将切片中的第一个元素改为1。但是对于没有存储的元素进行读写会产生panic,这是因为发生了越界访问。
对于字符串类型的切片,与int类型类似,我们可以使用new方法来创建一个切片。
ps := new([]string)
也会创建一个容量和包含元素个数都为0的切片,返回slice结构的起始地址并将其存储在ps中,此时的切片并没有分配底层数组,因为不能安全的对其中元素进行读写,可以使用append方法添加元素。
*ps = append(*ps,"eggo")
这样就会给切片开辟一个容量和包含元素个数都为1的底层数组。字符串类型由两部分组成,一个是内容起始地址,另一个是字符串的长度。
4.底层数组
切片的地址不一定指向底层数组的开头,可以将不同的切片关联到同一个数组。如下所示:
arr := [10]int{0,1,2,3,4,5,6,7,8,9}//数组容量声明后不可变
var s1 []int = arr[1:4]
var s2 []int = arr[7:]
s1的元素是arr索引1到4,左闭右开,s1的length为3,容量为从s1首元素对应的底层数组中的元素到底层数组结束,即为9(1-9)。同样的s2的容量为3。切片访问和修改的都是底层数组的元素,s1[3]是访问越界的,可以修改s1定义或者使用append方法扩大访问范围。当使用append方法添加的元素数量超过底层数组时,就需要开辟新的数组,将原元素copy过来再添加新的元素,length加对应个数,cap遵循一定的扩容规则:
- 如果扩容之前容量翻倍后还是小于添加元素猴的length,那么扩容之后的容量就是所需的最小容量。
- 否则要进一步判定
- 如果扩容前容量小于1024,直接翻倍
- 如果扩容前容量大于1024,则每次扩容四分之一
接下来判定需要的内存大小,扩容后所需内存大小由扩容后容量和元素类型有关,使用预估容量和元素大小的乘积作为所需的内存大小,并将其与Go语言调度器提前申请好的内存块进行比较,选出足够大且最接近的内存。