从零学 Go:切片中的应用实践
前文回顾
前面的文章主要介绍了 Go 语言中,切片的基本概念以及使用。本文将会继续介绍切片的相关应用于相关的函数。
切片
除了前面一篇文章介绍的切片初始化方式。还可以直接声明新的切片,这就类似于数组的初始化,但是不需要指定其大小,否则就变成了数组,样式如下所示:
var name []T
此时声明的切片并没有分配内存,我们可以在声明切片的同时对其进行初始化,如下例子所示:
ex := []int{1,2,3}
fmt.Printf("ex value is %v\n", ex)
fmt.Printf("ex len is %v\n", len(ex))
fmt.Printf("ex len is %v\n", cap(ex))
结果如下所示:
ex value is [1 2 3]
ex len is 3
ex cap is 3
此时声明的切片大小和容量都为 3。
Golang 中提供了 append
内建函数用于动态向切片添加成员,它将返回新的切片。如果当前切片的容量可以容纳更多的成员,添加的操作将在切片指向的原有数组上进行,这将会覆盖掉原有数组的值;如果当前切片的容量不足以容纳更多的成员,那么切片将会进行扩容,具体的扩容过程为:申请一个新的连续内存空间,空间大小一般是原有容量的 2 倍,然后将原来数组中的数据拷贝到新的数组中,同时将切片中的指针指向新的数组,最后将新的成员添加到新的数组中。我们将通过下面的例子来进行演示:
package main
import "fmt"
func main() {
arr1 := [...]int{1,2,3,4}
arr2 := [...]int{1,2,3,4}
sli1 := arr1[0:2] // 长度为 2,容量为 4
sli2 := arr2[2:4] // 长度为 2,容量为 2
fmt.Printf("sli1 pointer is %p, len is %v, cap is %v, value is %v\n", &sli1, len(sli1), cap(sli1), sli1)
fmt.Printf("sli2 pointer is %p, len is %v, cap is %v, value is %v\n", &sli2, len(sli2), cap(sli2), sli2)
newSli1 := append(sli1, 5)
fmt.Printf("newSli1 pointer is %p, len is %v, cap is %v, value is %v\n", &newSli1, len(newSli1), cap(newSli1), newSli1)
fmt.Printf("source arr1 become %v\n", arr1)
newSli2 := append(sli2, 5)
fmt.Printf("newSli2 pointer is %p, len is %v, cap is %v, value is %v\n", &newSli2, len(newSli2), cap(newSli2), newSli2)
fmt.Printf("source arr2 become %v\n", arr2)
}
上述例子的结果为:
sli1 pointer is 0xc00000c040, len is 2, cap is 4, value is [1 2]
sli2 pointer is 0xc00000c060, len is 2, cap is 2, value is [3 4]
newSli1 pointer is 0xc00007c020, len is 3, cap is 4, value is [1 2 5]
source arr1 become [1 2 5 4]
newSli2 pointer is 0xc00007c060, len is 3, cap is 4, value is [3 4 5]
source arr2 become [1 2 3 4]
通过上面的例子,我们可以发现,容量足够的 sli1
直接将 append
添加的新成员覆盖到原有数组 arr1
,而容量不够的 sli2
进行了扩容操作,申请了新的底层数组,不在原数组的基础上进行操作。在实际使用的过程中要千万记住这两种区别。
如果原有数组可以添加新的成员,即切片指向的数组后还有空间,但切片的容量已经饱和,此时进行 append
操作,同样会进行扩容,申请新的内存空间,如下例子所示:
arr3 := [...]int{1,2,3,4}
sli3 := arr3[0:2:2] // 长度为 2,容量为 2
fmt.Printf("sli3 pointer is %p, len is %v, cap is %v, value is %v\n", &sli3, len(sli3), cap(sli3), sli3)
newSli3 := append(sli3,5)
fmt.Printf("newSli3 pointer is %p, len is %v, cap is %v, value is %v\n", &newSli3, len(newSli3), cap(newSli3), newSli3)
fmt.Printf("source arr3 become %v\n", arr3)
对应的输出结果为:
sli3 pointer is 0xc00008e080, len is 2, cap is 2, value is [1 2]
newSli3 pointer is 0xc00006e0a0, len is 3, cap is 4, value is [1 2 5]
source arr3 become [1 2 3 4]
在上述代码中,我们指定了创建切片的第三个参数 cap
,cap
必须大于 end
,生成切片的容量将为 cap - begin
,限定了 sli3
的容量为 2。当进行 append
操作时,即使原有数组存在足够的空间,newSli3
还是指向新的数组空间。
为了方便切片的数据快速复制到另一个切片中,Golang 提供了内建的 copy
函数,它的使用样式如下:
copy(destSli, srcSli []T)
它的返回结果为实际发生复制的元素个数。如果要保证来源切片的数据都拷贝到目标切片,需要保证目标切片的长度大于等于来源切片的长度,否则将按照目标切面的长度大小进行拷贝。
小结
总得来说 Go 语言中数组和切片有如下的区别:
- 切片是指针类型,数组是值类型
- 数组的长度是固定的,而切片不是(切片是动态的数组)
- 切片比数组多一个属性:容量(cap)
- 切片的底层是数组
本文主要介绍了切片的一些函数,切片是 Go 中提供了一种灵活,功能强悍的内置类型(“动态数组”)。与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。
- 点赞
- 收藏
- 关注作者
评论(0)