Go 语言函数式编程系列教程(十) —— 数据类型篇:在数组切片中动态增删元素

栏目: Go · 发布时间: 6年前

内容简介:切片比数组更强大之处在于支持动态增加元素,甚至可以在容量不足的情况下自动扩容,关于容量我们在上篇教程中已经简单提及过,在切片类型中,元素个数和实际可分配的存储空间是两个不同的值,元素的个数即切片的实际长度,而可分配的存储空间就是切片的容量。一个切片的容量初始值根据创建方式的不同而不同:所以,通常一个切片的长度值小于等于其容量值,我们可以通过 Go 语言内置的

动态增加元素

切片比数组更强大之处在于支持动态增加元素,甚至可以在容量不足的情况下自动扩容,关于容量我们在上篇教程中已经简单提及过,在切片类型中,元素个数和实际可分配的存储空间是两个不同的值,元素的个数即切片的实际长度,而可分配的存储空间就是切片的容量。

一个切片的容量初始值根据创建方式的不同而不同:

make

所以,通常一个切片的长度值小于等于其容量值,我们可以通过 Go 语言内置的 cap() 函数和 len() 函数来获取某个切片的容量和实际长度:

var oldSlice = make([]int, 5, 10)

fmt.Println("len(oldSlice):", len(oldSlice))
fmt.Println("cap(oldSlice):", cap(oldSlice))

程序运行结果如下:

len(oldSlice): 5
cap(oldSlice): 10

此时,切片 oldSlice 的默认值是 [0 0 0 0 0] ,我们可以通过 append() 函数向切片追加新元素:

newSlice := append(oldSlice, 1, 2, 3)

将返回的新切片赋值给 newSlice ,此时 newSlice 的长度是 8,容量是 10,切片值是:

[0 0 0 0 0 1 2 3]

函数 append() 的第二个参数是一个不定参数,我们可以按自己需求添加若干个元素(大于等于1个),甚至直接将一个数组切片追加到另一个数组切片的末尾:

appendSlice := []int{1, 2, 3, 4, 5}
newSlice := append(oldSlice, appendSlice...)  // 注意末尾的 ... 不能省略

如果追加的元素个数超出 oldSlice 的默认容量,则底层会自动进行扩容:

newSlice := append(oldSlice, 1, 2, 3, 4, 5, 6)
fmt.Println(newSlice)
fmt.Println(len(newSlice))
fmt.Println(cap(newSlice))

此时 newSlice 的长度变成了 11,容量变成了 20,需要注意的是 append() 函数并不会改变原来的切片,而是会生成一个容量更大的切片,然后把原有的元素和新元素一并拷贝到新切片中,默认情况下,扩容后新切片的容量将会是原切片容量的 2 倍,如果还不足以容纳新元素,则按照同样的操作继续扩容,直到新容量不小于原长度与要追加的元素数量之和。但是,当原切片的长度大于或等于 1024 时,Go 语言将会以原容量的 1.25 倍作为新容量的基准。

因此,如果实现能预估切片的容量并在初始化时合理地设置容量值,可以大幅降低切片内部重新分配内存和搬送内存块的操作次数,从而提高程序性能。

内容复制

切片类型还支持 Go 语言的另一个内置函数 copy() ,用于将元素从一个数组切片复制到另一个数组切片。如果加入的两个数组切片不一样大,就会按其中较小的那个数组切片的元素个数进行复制。下面的示例展示了 copy() 函数的行为:

slice1 := []int{1, 2, 3, 4, 5} 
slice2 := []int{5, 4, 3}

// 复制 slice1 到 slice 2
copy(slice2, slice1) // 只会复制 slice1 的前3个元素到 slice2 中
// 复制 slice2 到 slice 1
copy(slice1, slice2) // 只会复制 slice2 的 3 个元素到 slice1 的前 3 个位置

动态删除元素

切片除了支持动态增加元素之外,还可以动态删除元素,在切片中动态删除元素可以通过多种方式实现(其实是通过切片实现的「伪删除」):

slice3 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
slice3 = slice3[:len(slice3) - 5]  // 删除 slice3 尾部5个元素
slice3 = slice3[5:]  // 删除 slice3 头部 5 个元素

此时切片 slice3 的所有元素被删除,长度是0,容量也变成 5,注意这里不是自动缩容,而是第二个切片容量计算逻辑决定的。

此外,还可以通过上述介绍的 append 函数和 copy 函数实现切片元素的「删除」:

slice3 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

slice4 := append(slice3[:0], slice3[3:]...)  // 删除开头三个元素
slice5 := append(slice3[:1], slice3[4:]...)  // 删除中间三个元素
slice6 := append(slice3[:0], slice3[:7]...)  // 删除最后三个元素

slice7 := slice3[:copy(slice3, slice3[3:])]  // 删除开头前三个元素

append 相对好理解一些, copy 之所以可以用于删除元素,是因为其返回值是拷贝成功的元素个数,我们可以根据这个值完成新切片的设置从而达到「删除」元素的效果。

和动态增加元素一样,原切片的值并没有变动,而是创建出一个新的内存空间来存放新切片并将其赋值给其它变量。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Domain-Driven Design

Domain-Driven Design

Eric Evans / Addison-Wesley Professional / 2003-8-30 / USD 74.99

"Eric Evans has written a fantastic book on how you can make the design of your software match your mental model of the problem domain you are addressing. "His book is very compatible with XP. It is n......一起来看看 《Domain-Driven Design》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

html转js在线工具
html转js在线工具

html转js在线工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具