golang中的slice表示一个可变长度的数组,声明为 []T
,其中T是类型。 关于数组请参见:golang中的数组
slice跟数组紧密相连,slice有三个属性,分别是:指针、长度(len)和容量(cap),其中指针指向底层数组,所以它天然依赖底层数组。从某种意义上来讲,slice更像一个结构体,这个结构体里面必然包含一个指针。
arr := [...]int{1, 2, 3, 4, 5}
slice1 := arr[:3]
for i := range slice1 {
slice1[i]++
}
fmt.Println(slice1) // [2 3 4]
fmt.Println(arr) // [2 3 4 4 5]
对slice中的元素的值加一,影响到了数组arr中的值,因为 arr就是slice1的底层数组,slice1中的指针指向arr。
如果对字符串(等同于[]byte)使用 x[m:m]
,返回的还是一个字符串(其实还是一个[]byte),但如果对数组使用 x[m:m]
,返回的就不是数组了,而是slice。
注意一下初始化数组和初始化slice的方式
// 初始化数组,编译器会自动解析数组长度
arr := [...]int{1, 2, 3, 4, 5}
// 初始化slice,没有指定长度。golang会创建一个固定长度的底层数组,并用slice指向它
slice := []int{1, 2, 3, 4, 5}
slice之间无法直接比较,因为slice的本质是一个固定范围的指针,并不是直接的元素,并且底层数组还有可能会改变。
不过,slice可以和nil作对比。
if slice == nil {}
nil是slice的零值,它的没有底层数组,len为0,cap为0。
var s []int // len(s) == 0, s == nil
s = []int(nil) // len(s) == 0, s== nil
s = []int{} // len(s) == 0, s != nil
所以检查一个slice是否为空,应该使用 len(s) == 0
, 而不是与nil
作比较。
当s为nil时,len(s)
也是安全的,返回0。
make([]T, len)
make([]T, len, cap)
如果我们一开始就知道要创建多长的slice,例如长度为x,那么这样写会更高效,因为可以避免在后续的append中重新创建底层数组。
make([]T, 0, x)
append([]T x, T y)
append的实现原理是,先检查当前slice是否有足够的容量,如果有,则定义一个新的slice,和之前的slice指向相同的底层数组,然后将新元素复制到新slice的新位置,并返回新slice。如果容量不足,则创建一个容量更大的slice(按照某种算法扩大容量),同时也会创建一个新的底层数组,将原slice的值复制过来,再将新元素复制到新slice的新位置。
所以,append每次都会返回一个新的slice,但我们无法确定旧slice和新slice是否指向同一个底层数组。
因此,使用append函数的正确姿势是
slice = append(slice, item)
实际上,我们对任何可能会生成新slice的函数,都要再把返回值赋给原变量。
slice的本质是一段固定范围的指针,append函数会改变slice的长度(len),所以需要返回一个新的slice。这么看来,slice并不像是严格意义上引用类型,更像是一种聚合类型
type IntSlice struct {
ptr *int
len, cap int
}
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/daguanjia11/article/details/121237754
内容来源于网络,如有侵权,请联系作者删除!