栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Go语言

slice 切片详解

Go语言 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

slice 切片详解

1、slice 实现原理         Slice又称动态数组,依托数组实现,可以方便的进行扩容、传递等,实际使用中比数组更灵活。         Slice 依托数组实现,底层数组对用户屏蔽,在底层数组容量不足时可以实现自动重分配并生成新的 Slice 。
源码包中 src/runtime/slice.go:slice 定义了 Slice 的数据结构: type slice struct {
    array unsafe.Pointer
    len   int
    cap   int
}

 从数据结构看Slice很清晰, array指针指向底层数组,len表示切片长度,cap表示底层数组容量。

1.2 使用make创建Slice 使用 make 来创建 Slice 时,可以同时指定长度和容量,创建时底层会分配一个数组,数组的长度即容量。 例如,语句 slice := make([]int, 5, 10) 所创建的 Slice ,结构如下图所示:

 

该 Slice 长度为 5 ,即可以使用下标 slice[0] ~ slice[4] 来操作里面的元素, capacity 为 10 ,表示后续向 slice 添加新的元素时可以不必重新分配内存,直接使用预留内存即可。   1.3 使用数组创建Slice 使用数组来创建 Slice 时, Slice 将与原数组共用一部分内存。 例如,语句 slice := array[5:7] 所创建的 Slice ,结构如下图所示: 切片从数组 array[5] 开始,到数组 array[7] 结束(不含 array[7] ),即切片长度为 2 ,数组后面的内容都作为切片的预留内存, 即capacity 为 5 。 数组和切片操作可能作用于同一块内存,这也是使用过程中需要注意的地方。

 

2、Slice 扩容 2.1、slice 扩容影响原数组 append 函数会改变 slice 所引用的数组的内容,从而影响到引用同一数组的其它 slice。
但当 slice 中没有剩余空间(即(cap-len) == 0)时,此时将动态分配新的数组空间。返回的slice 数组指针将指向这个空间,而原数组的内容将保持不变;其它引用此数组的 slice 则不受影响。
func testSlice() {
	var ar = [...]byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}
	// 打印结果:abcdefgh
	fmt.Printf("%s n", ar)
	slice1 := ar[2:5]
	// 注:append 函数会改变 slice 所引用的数组的内容,从而影响到引用同一数组的其它 slice。
	// 但当 slice 中没有剩余空间(即(cap-len) == 0)时,此时将动态分配新的数组空间。返回的
	// slice 数组指针将指向这个空间,而原数组的内容将保持不变;其它引用此数组的 slice 则不受影响。
	slice1 = append(slice1, 'A')
	// 打印结果:cdeA
	fmt.Printf("%s n", slice1)
	// 打印结果:abcdeAgh
	fmt.Printf("%s n", ar)
}
2.2、slice 扩容注意事项 使用 append 向 Slice 追加元素时,如果 Slice 空间不足,将会触发 Slice 扩容,扩容实际上是重新分配一块更大的内存,将原Slice数据拷贝进新 Slice ,然后返回新 Slice ,扩容后再将数据追加进去。 例如,当向一个 capacity 为 5 ,且 length 也为 5 的 Slice 再次追加 1 个元素时,就会发生扩容,如下图所示: 扩容操作只关心容量,会把原 Slice 数据拷贝到新 Slice ,追加数据由 append 在扩容结束后完成。上图可见,扩容后新的 Slice 长 度仍然是 5 ,但容量由 5 提升到了 10 ,原 Slice 的数据也都拷贝到了新 Slice 指向的数组中。 2.3、扩容容量的选择遵循以下规则:         1、如果原Slice 容量小于 1024 ,则新 Slice 容量将扩大为原来的 2 倍;         2、如果原Slice 容量大于等于 1024 ,则新 Slice 容量将扩大为原来的 1.25 倍; 2.4、使用append()向Slice添加一个元素的实现步骤如下:         1、假如Slice 容量够用,则将新元素追加进去, Slice.len++ ,返回原 Slice         2、原Slice 容量不够,则将 Slice 先扩容,扩容后得到新 Slice         3、将新元素追加进新Slice , Slice.len++ ,返回新的 Slice 。

 

3、总结         1、创建切片时可根据实际需要预分配容量,尽量避免追加过程中扩容操作,有利于提升性能;         2、切片拷贝时需要判断实际拷贝的元素个数         3、谨慎使用多个切片操作同一个数组,以防读写冲突         4、每个切片都指向一个底层数组         5、 每个切片都保存了当前切片的长度、底层数组可用容量         6、 使用 len() 计算切片长度时间复杂度为 O(1) ,不需要遍历切片         7、使用cap() 计算切片容量时间复杂度为 O(1) ,不需要遍历切片         8、通过函数传递切片时,不会拷贝整个切片,因为切片本身只是个结构体而已         9、使用append() 向切片追加元素时有可能触发扩容,扩容后将会生成新的切片
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/990795.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号