crossain

导航

Go语言中的slice

1.Go语言中slice的组成

Go语言中的切片由三部分组成,第一部分是切片元素的存储位置,第二部分是切片中元素的数量,第三部分是切片的容量。

2.slice的创建

声明一个整型切片ints如下图所示:

var ints []int

变量Ints由三部分组成:

image-20220108150404013

切片元素要存在一段连续的内存中,实际上与数组相同,data就是该切片对应底层数组的起始地址,目前只分配了切片结构,还没有分配底层数组,因此这里的data值为nil,存储元素个数和容量都为0。如果使用make来创建切片,如下所示:

var ints []int = make([]int,2,5)

系统就会开辟一段容纳5个元素的内存作为ints的底层数组,并将该数组中的每一个元素初始化为0。

image-20220108150716336

由于make创建的切片中只包含两个元素,因此当前的Ints为:

image-20220108150846643

3.切片的使用

可以使用append方法向切片中添加元素。

ints = append(ints,1)

添加后切片变为:

image-20220108151047339

包含的元素个数(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语言调度器提前申请好的内存块进行比较,选出足够大且最接近的内存。

posted on 2022-01-08 16:01  crossain  阅读(68)  评论(0编辑  收藏  举报