本文已参与「新人创作礼」活动, 一起开启掘金创作之路
引言
Go中的切片(slice)数据结构对比java来说,还是一个稍微新的概念,但是数组对于我们来说确实一个非常熟悉的数据结构,但是由于slice在go编程中使用的非常频繁,所以今天我们来详细的总结一下这个数据结构。
slice是什么
切片和数组一样,也是可以容纳若干类型相同的元素的容器。我们来看下,go语言中slice中的定义:
type slice struct {
array unsafe.Pointer
len int
cap int
}
// unsafe.Pointer 类型如下
// Pointer represents a pointer to an arbitrary type. There are four special operations
// available for type Pointer that are not available for other types:
// - A pointer value of any type can be converted to a Pointer.
// - A Pointer can be converted to a pointer value of any type.
// - A uintptr can be converted to a Pointer.
// - A Pointer can be converted to a uintptr.
// Pointer therefore allows a program to defeat the type system and read and write
// arbitrary memory. It should be used with extreme care.
type Pointer *ArbitraryType
切片 GO 的一种数据类型 , 是对数组的一个连续片段的引用
切片的底层结构是一个结构体,对应有三个参数
从上面的结果中我们,可能存在下面几个问题:
1、容量为什么是4?
上述代码 arr[4:6] 指的是将数组的下标为 4 开始的位置,下标为 6 的为结束位置,这里是不包含 6 自己的
根据 GO 中切片的原理,用数组复制给到切片的时候,若复制的数组元素后面还有内容的话,则后面的内容都作为切片的预留内存
即得到上述的结果,len == 2, cap == 4
不过这里还是要注意,切片元素对应的地址,还是这个数组元素对应的地址,使用的时候需要小心。
2、当我们重新定义一个新的切片的时候,为什么是8?
这是因为我们 myslice = myslice[:cap(myslice)],重新生成一个新的数组的时候,结构如下:
slice扩容原理
myslice:=make([]int,4,4)
fmt.Printf("长度:%d;容量:%d \n",len(myslice),cap(myslice))
myslice = append(myslice,1)
fmt.Printf("新长度:%d;新容量:%d \n",len(myslice),cap(myslice))
结果:
长度:4;容量:4
新长度:5;新容量:8
从上面的结果中,可以直观的感受到,当我们想切片中增加给元素的时候,切片的容量从原理的4扩容到8.
我们来看看切片扩容的规则是这样的:
如果原来的切片容量小于 1024 那么新的切片容量就会扩展成原来的 2 倍
如果原切片容量大于等于 1024 那么新的切片容量就会扩展成为原来的 1.25 倍
我们再来梳理一下上述扩容原理的步骤是咋弄的
上述切片扩容,大致分为如下 2 种情况:
1、添加的元素,加入到切片中,若原切片容量够 那么就直接添加元素,且切片的 len ++ ,此处的添加可不是直接赋值,可是使用 append 函数的方式。
2、若原切片容量不够,则先将切片扩容,再将原切片数据追加到新的切片中
空切片和nil切片
func main(){
// 是一个空对象
var mys1 []int
// 是一个对象,对象里面是一个切片,这个切片没有元素
var mys2 = []int{}
json1, _ := json.Marshal(mys1)
json2, _ := json.Marshal(mys2)
fmt.Println(string(json1))
fmt.Println(string(json2))
}
结果:
null
[]
原因是这样的:
- mys1 是一个空对象
- mys2 不是一个空对象,是一个正常的对象,但是对象里面的为空
总结
切片在go语言的日常编程中使用的非常频繁,如果我们能理解切片的一些底层原理和正确的使用方式,可以提高我们程序的运行效率。