爱上开源之golang入门至实战第四章-切片(Slice)

爱上开源之golang入门至实战第四章-切片(Slice)前言Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go 中提供了一种灵活,功能强悍的内置类型切片(“动态数组”

大家好,欢迎来到IT知识分享网。

前言

Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go 中提供了一种灵活,功能强悍的内置类型切片(“动态数组”),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。

在go语言里,切边是一个应用极为广泛的数据对象;所以在学习golang的过程中,一定要好好地掌握切片的相关知识。

爱上开源之golang入门至实战第四章-切片(Slice)

4.2.2.1 声明切片

你可以声明一个未指定大小的数组来定义切片:

var slice_01 []type

在语法上和上面的数组比较的相似;差别就在于切片不需要说明长度。

也可以使用 make() 函数来创建切片:

var slice_01 []type = make([]type, len) 或为 slice_01 := make([]type, len)

在切片的make声明和初始话的方式中;也可以指定容量,

比如

make([]T, length, capacity)

其中 capacity 为可选参数。通过上面的方式就可以声明和初始话一个容量大小为capacity,当前长度为length的切片,切片的用法如何使用过python语言的话,这个切片的数据类型可以实现的功能基本上和python语言里slice函数的非常的相似

需要说明,slice 并不是数组或数组指针。它通过内部指针和相关属性引⽤数组⽚段,以实现变⻓⽅案。

runtime.h

#runtime.h struct Slice { // must not move anything byte* array; // actual data uintgo len; // number of elements uintgo cap; // allocated number of elements }; 

以上是golang里slice的源代码片段;可以通过这个slice的结构体源代码的定义,发现在golang里,slice是通过内部属性和指针的方式,来实现了变长的方案。

• 引⽤类型。但⾃⾝是结构体,值拷⻉传递。

• 属性 len 表⽰可⽤元素数量,读写操作不能超过该限制。

• 属性 cap 表⽰最⼤扩张容量,不能超出数组限制。

• 如果 slice == nil,那么 len、cap 结果都等于 0。

4.2.2.2 切片初始化

slice_01 :=[] int {1,2,3 }

通过初始化语句:=直接进行初始化, 其中[] 表示是切片类型,{1,2,3} 初始化值依次是 1,2,3,其 cap=len=3。通过以上语句,实现了初始化slice_01对象为切片对象,并且长度和容量都为3;其中元素依次为1,2,3的int切片。

爱上开源之golang入门至实战第四章-切片(Slice)

4.2.2.3 len和cap

切片是可索引的,并且可以由 len() 方法获取长度。切片提供了计算容量的方法 cap() 可以测量切片最长可以达到多少。

x := make([]int,3,5) fmt.Printf("len=%d cap=%d slice=%v**\n**",len(x),cap(x),x) ====OUTPUT==== len=3 cap=5 slice=[0 0 0]

一个切片在未初始化之前默认为 nil,长度为 0,如下:

var x []int fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x) ====OUTPUT==== len=0 cap=0 slice=[]

4.2.2.4 slice

s := arr[startIndex:endIndex] 

将 arr 中从下标 startIndex 到 endIndex-1 下的元素创建为一个新的切片。

s := arr[startIndex:] 

默认 endIndex 时将表示一直到arr的最后一个元素。

s := arr[:endIndex] 

默认 startIndex 时将表示从 arr 的第一个元素开始。

样例

data := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} expression slice len cap ------------+----------------------+------+------- data[:6:8] [0 1 2 3 4 5] 6 8 data[5:] [5 6 7 8 9] 5 5 data[:3] [0 1 2] 3 10 data[:] [0 1 2 3 4 5 6 7 8 9] 10 10 data[:11:12] Error 

读写操作实际⺫标是底层数组,只需注意索引号的差别。

data := [...]int{0, 1, 2, 3, 4, 5} s := data[2:4] s[0] += 1020 s[1] += 2020 fmt.Println(s) fmt.Println(data)

输出:

[1022 2023] [0 1 1022 2023 3 4 5]
s1 := s[startIndex:endIndex] 

通过切片 s 初始化切片 s1。

对于是基于已有 slice 创建新 slice 对象,特别注意要保证切片的 cap 允许范围内调整属性。

s := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} s1 := s[2:5] // [2 3 4] len=3 cap=8 s2 := s1[2:6:7] // [4 5 6 7] len=3 cap=5 s3 := s2[3:6] // Error

切片操作后新对象依旧指向原底层数组。

 s := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} s1 := s[2:5] // [2 3 4] s1[2] = 1020 fmt.Println(s1) s2 := s1[2:6] // [1020 5 6 7] s2[0] = 2010 s2[3] = 2020 fmt.Println(s2) fmt.Println(s) ====OUTPUT==== [2 3 1020] [2010 5 6 2020] [2 3 2010] [0 1 2 3 2010 5 6 2020 8 9]
爱上开源之golang入门至实战第四章-切片(Slice)

4.2.2.5 append

向 slice 尾部添加数据,返回新的 slice 对象。

s := make([]int, 0, 5) fmt.Printf("%p\n", &s) s2 := append(s, 1) fmt.Printf("%p\n", &s2) fmt.Println(s, s2) ====OUTPUT===== 0xc000004498 0xc0000044c8 [] [1] 
data := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} s := data[:3] s2 := append(s, 100, 200) // 添加多个值。 fmt.Println(data) fmt.Println(s) fmt.Println(s2) ====OUTPUT===== [0 1 2 100 200 5 6 7 8 9] [0 1 2] [0 1 2 100 200]

⼀旦超出原 slice.cap 限制,就会重新分配底层数组,即便原数组并未填满。

data := [...]int{0, 1, 2, 3, 4, 10: 0} s := data[:2:3] s = append(s, 100, 200) // ⼀次 append 两个值,超出 s.cap 限制。 fmt.Println(s, data) // 重新分配底层数组,与原数组⽆关。 fmt.Println(&s[0], &data[0]) // ⽐对底层数组起始指针。 ====OUTPUT===== [0 1 100 200] [0 1 2 3 4 5 6 7 8 9] 0xc0001e21e0 0xc000022af0

从输出结果可以看出,append 后的 s 重新分配了底层数组,并复制数据。如果只追加⼀个值,则不会超过 s.cap 限制,也就不会重新分配

将上面程序改成下面的

data := [...]int{0, 1, 2, 3, 4, 10: 0} s := data[:2:3] s = append(s, 100) fmt.Println(s, data) fmt.Println(&s[0], &data[0]) s = append(s, 200) fmt.Println(s, data) // 重新分配底层数组,与原数组⽆关。 fmt.Println(&s[0], &data[0]) // ⽐对底层数组起始指针。 ====OUTPUT===== [0 1 100] [0 1 100 3 4 5 6 7 8 9] 0xc00011e9b0 0xc00011e9b0 [0 1 100 200] [0 1 100 3 4 5 6 7 8 9] 0xc00013dd70 0xc00011e9b0

4.2.2.6 copy

copy 函数可以实现在两个 slice 间复制数据,复制⻓度以 len⼩的为准。两个 slice 可指向同⼀底层数组,允许元素区间重叠。

data := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} s := data[8:] s2 := data[:5] copy(s2, s) fmt.Println(s2) fmt.Println(data) ====OUTPUT===== [8 9 2 3 4] [8 9 2 3 4 5 6 7 8 9]

4.2.2.7 扩容

当切片的长度超过容量的情况下,切片会进行扩容操作;通常以 2 倍容量重新分配底层数组。在⼤批量添加数据时,建议⼀次性分配⾜够⼤的空间,以减少内存分配和数据复制开销。或初始化⾜够⻓的 len 属性,改⽤索引号进⾏操作。及时释放不再使⽤的 slice 对象,避免持有过期数组,造成 GC ⽆法回收

结束语

在go语言里,切边是一个应用极为广泛的数据对象;所以在学习golang的过程中,一定要好好地掌握切片的相关知识。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/77314.html

(0)
上一篇 2024-08-06 17:00
下一篇 2024-08-07 11:45

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

关注微信