go slice使用
1. 简介
在go中,slice
是一种动态数组类型,其底层实现中使用了数组。slice
有以下特点:
*slice
本身并不是数组,它只是一个引用类型,包含了一个指向底层数组的指针,以及长度和容量。
*slice
的长度可以动态扩展或缩减,通过append
和copy
操作可以增加或删除slice
中的元素。
*slice
的容量是指在底层数组中slice
可以继续扩展的长度,容量可以通过make
函数进行设置。
Slice 的底层实现是一个包含了三个字段的结构体:
type`slice`struct {
ptr uintptr // 指向底层数组的指针
len int // slice 的长度
cap int // slice 的容量
}
当一个新的slice
被创建时,Go会为其分配一个底层数组,并且把指向该数组的指针、长度和容量信息存储在slice
结构体中。底层数组的长度一般会比slice
的容量要大,以便在append
操作时有足够的空间存储新元素。
当一个slice
作为参数传递给函数时,其实是传递了一个指向底层数组的指针,这也就意味着在函数内部对slice
的修改也会反映到函数外部。
在进行切片操作时,slice 的指针和长度信息不会发生变化,只有容量信息会发生变化。如果切片操作的结果仍然是一个 slice,那么它所引用的底层数组仍然和原来的slice
是同一个数组。
需要注意的是,当一个slice
被传递给一个新的变量或者作为参数传递给函数时,并不会复制底层数组,而是会共享底层数组。因此,如果对一个slice
的元素进行修改,可能会影响到共享底层数组的其他slice
。如果需要复制一个slice
,可以使用copy
函数。
2. 使用
slice
的使用包括定义、初始化、添加、删除、查找等操作。
2.1 slice定义
slice
是一个引用类型,可以通过声明变量并使用make()函数来创建一个slice
:
var sliceName []T
sliceName := make([]T, length, capacity)
其中,T代表该切片可以保存的元素类型,length代表预留的元素数量,capacity代表预分配的存储空间。
2.2 初始化
slice
有两种初始化的方式:声明时初始化和使用append()函数初始化:
// 声明时初始化
sliceName := []T{value1, value2, ..., valueN}
// 使用append()函数进行初始化
sliceName := make([]T, 0, capacity)
sliceName = append(sliceName, value1, value2, ..., valueN)
2.3 获取slice元素
slice
中的元素可以通过索引的方式来获取,与c/c++类似,go的索引也是从0开始的:
sliceName[index]
2.4 添加元素到slice中
可以通过使用append()函数将元素添加到slice
中。如果slice
的容量不足,则会自动扩展。语法如下:
sliceName = append(sliceName, value1, value2, ..., valueN)
2.5 删除slice中的元素
可以使用append()函数和切片操作来从slice
中删除元素。使用append()函数时,需要将带有要删除元素的切片放在最后。语法如下:
// 通过切片操作删除元素
sliceName = append(sliceName[:index], sliceName[index+1:]...)
// 通过append()函数删除元素
sliceName = append(sliceName[:index], sliceName[index+1:]...)
如上所见,二者的表现形式是一样的,但内部实现是不同的:
- 使用append()进行删除的方式,实际上是将后面的元素向前移动一个位置,然后通过重新切片的方式来删除最后一个元素。这种方式会创建一个新的底层数组,并将原来的元素复制到新的数组中,因此在删除多个元素时可能会导致内存分配和复制开销较大,影响性能
- 使用切片语法进行删除,底层数组中被删除元素的位置仍然存在,但是这些位置不再包含有效的数据。这种方式的性能比使用append()进行删除要好,尤其是在删除多个元素时,因为它不需要创建新的底层数组,也不需要复制元素。但是,这种方式可能会导致底层数组中存在大量未使用的空间,浪费内存
需要注意的是,在切片中删除元素时,会重新分配内存并复制元素,因此删除元素的成本会相对较高。为了减少内存分配和复制元素的次数,可以使用copy
函数将后面的元素复制到前面,然后将切片的长度减少。具体实现方法可以参考下面的:
// 删除切片中指定位置的元素
func removeElement(slice []int, index int) []int {
copy(slice[index:], slice[index+1:])
return slice[:len(slice)-1]
}
2.6 查找slice中的元素
可以使用for和range遍历slice
来实现元素查询:
// 使用for循环和range关键字遍历Slice
for index, value := range sliceName {
if value == targetValue {
// 找到了目标元素
break
}
}
2.7 切片操作
可以使用切片操作来获取子切片,操作如下:
// 切片操作:获取从第i个元素到第j个元素的子切片
sliceName[i:j]
// 切片操作:获取从第i个元素到第j个元素,且容量为k的子切片
sliceName[i:j:k]
3. 关于slice扩容
在Go语言中,slice
会随着元素的增加而动态扩容。当容量不足时,slice
会自动重新分配内存,将原有元素复制到新的底层数组中,并在新数组后面添加新的元素。
slice
的扩容机制可以描述为:当slice
的长度超过了底层数组的容量时,Go语言会按照一定的策略重新分配一块更大的内存,并将原来的元素复制到新的内存中,然后再添加新元素。具体的策略如下:
- 如果新长度(即len(s)+1)小于等于原长度(即cap(s)),则
slice
不需要扩容,直接添加元素即可。 - 如果新长度大于原长度且小于原长度的两倍(即 cap(s)*2),则新
slice
的容量就是原来的两倍,也就是说将底层数组扩容为原来的两倍,并将原来的元素复制到新的数组中。 - 如果新长度大于原长度的两倍,会尝试使用新长度作为容量,如果仍然不够,则按照扩容倍数(默认是 2)来扩容。
需要注意的是,slice
扩容是一个开销比较大的操作,因为需要重新分配内存、复制数据等。所以在编写代码时应该尽可能地减少slice
扩容的次数,以提高程序的性能。
声明:本作品采用署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)进行许可,使用时请注明出处。
Author: mengbin
blog: mengbin
Github: mengbin92
cnblogs: 恋水无意
go slice使用的更多相关文章
- Matlab slice方法和包络法绘制三维立体图
前言:在地球物理勘探,流体空间分布等多种场景中,定位空间点P(x,y,x)的物理属性值Q,并绘制三维空间分布图,对我们洞察空间场景有十分重要的意义. 1. 三维立体图的基本要件: 全空间网格化 网格节 ...
- jQuery之常用且重要方法梳理(target,arguments,slice,substring,data,trigger,Attr)-(一)
1.jquery data(name) data() 方法向被选元素附加数据,或者从被选元素获取数据. $("#btn1").click(function(){ $(" ...
- js url.slice(star,end) url.lastIndexOf('/') + 1, -4
var url = '"http://60.195.252.25:15518/20151228/XXSX/作三角形的高.mp4")' document.title = url.sl ...
- JavaScript中的slice,splice,substr,substring,split的区别
万恶的输入法,在sublime中会显示出繁体字,各位看官见谅. 1.slice()方法:该方法在数组和string对象中都拥有. var a = [1,2,3,4,5,6]; var s = 'thi ...
- Max double slice sum 的解法
1. 上题目: Task description A non-empty zero-indexed array A consisting of N integers is given. A tripl ...
- js中substr,substring,slice。截取字符串的区别
substr(n1,n2) n1:起始位置(可以为负数) n2:截取长度(不可以为0,不可以为负数,可以为空) 当n1为正数时,从字符串的n1下标处截取字符串(起始位置),长度为n2. 当n1为负数时 ...
- JS 中 Array.slice() 和 Array.splice()方法
slice slice()就是对应String的substring()版本,它截取Array的部分元素,然后返回一个新的Array: var arr = ['A', 'B', 'C', 'D', 'E ...
- 【javascript 技巧】Array.prototype.slice的妙用
Array.prototype.slice的妙用 开门见山,关于Array 的slice的用法可以参考这里 http://www.w3school.com.cn/js/jsref_slice_arra ...
- golang中的slice翻转存在以及map中的key判断
//slice翻转 func stringReverse(src []string){ if src == nil { panic(fmt.Errorf("the src can't be ...
- jQuery中slice()用法总结
<!DOCTYPE html> <html> <head lang="en"> <meta charset="utf-8&quo ...
随机推荐
- 配置git到码云
一.安装完git之后,对项目文件点击右键选择Git Base Here #Git 全局设置用户名与邮箱 git config --global user.name "" git c ...
- Codeforces Round #803 (Div. 2) A-D 刚vp完还没补题
Codeforces Round #803 (Div. 2) 2022/7/24 上午VP 传送门:https://codeforces.com/contest/1698 A. XOR Mixup 随 ...
- LockSupport实现线程同步
public class LockSupport01 { static Thread t1 = null, t2 = null; public static void main(String[] ar ...
- Linux下C语言程序的内存布局
在<虚拟地址空间以及编译模式>一节中讲到,虚拟地址空间在32位环境下的大小为 4GB,在64位环境下的大小为 256TB,那么,一个C语言程序的内存在整个地址空间中是如何分布的呢?数据在哪 ...
- 随便记录一些使用IDEA在ssm阶段的踩过的坑
重命名中括号问题:需要重命名模块+目录 Intellij idea 报错:Error : java 不支持发行版本5_灵颖桥人的博客-CSDN博客_不支持发行版本5 idea中的目标字节码版本总是自动 ...
- Go_day06
Go基础语法 错误和异常 什么是错误error 错误是指可鞥出现问题的地方出现了问题,这种情况在预料之中 func main() { //打开一个文件 file, err := os.Open(&qu ...
- FCARM - Output Name not specified, please check 'Options for Target - Utilit问题
FCARM - Output Name not specified, please check 'Options for Target - Utilit问题 按照书上说明按步操作,但是书上是按照kei ...
- 初探redis缓存击穿、穿透、雪崩问题
现分析Redis缓存使用过程失效的一些问题,在有缓存的情况下,查询数据的顺序是先查询缓存,如果查询到数据则直接返回数据,如果没有查询到数据,则到数据库中查询,数据库中有数据的话,将查询出的数据写到缓存 ...
- IBM 双队列管理器,双向传输
1. 建立队列管理器 建立[test01][test02]两个队列管理器,一直下一步即可,端口号不能一致(需要记住设置的端口号,后面会用到) [test01]端口号 1414 [test02]端口号 ...
- baodoumi yml配置
上次因为自增,发现配置没生效, 修改后如下 # 配置mybatis-plus, 参考:com.baomidou.mybatisplus.autoconfigure.MybatisPlusPropert ...