Golang Slice

Slice介绍

Slice也叫做切片,是golang中最为常用的一个结构之一,跟数组相比,它更加灵活便利,拥有自动扩容策略,但是也存在着一些比较容易被忽略的坑点,文章会先介绍Slice的基本使用方式,接着会介绍Slice的内部实现,最后会总结一些我遇到过的坑点(这一点可能会持续更新)~

使用方式

Slice切片初始化

slice的初始化和数组的初始化方式特别相似,但是这是两种完全不同的数据结构,数组的容量大小是不允许被调整的。

//slice的初始化
s2 := []int{}
s3 := []int{1, 2, 3}
s4 := make([]int, 0, 10) //预分配一些容量,make的第二个参数为len,第三个参数为capacity

//数组初始化
s5 := [3]int{1, 2, 3}
s6 := [...]int{1, 2, 3} //自动推导
基本操作
s1 := make([]int, 10) //make时指定len时会给定默认值,如果打印s1,输出结果是10个0,感兴趣可以试一试
//slice的遍历
for index, value := range s1 {
    //do something
}

//slice追加
s1 := append(s1, 1)
//清空slice
s1 := s1[0:0]
//获取slice的长度
length := len(s1)
//获取slice的容量
capacity := cap(s1)
//获取slice子串
s2 := s1[0:10] //这里是左闭右开的原则
//将s1或s1的字串追加到s2中
s2 := append(s2, s1[0:10]...) //...是一个操作符,会将整个切片拆开append到s2中

slice的内部结构

在运行时切片的结构使用如下SliceHeader表示的,分别有指向数组的指针、切片长度、切片容量
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k4r5GrYQ-1594266842810)(https://yunpan.oa.tencent.com/note/api/file/getImage?fileId=5f06916e6f0b9316e2670201)]

type SliceHeader struct {
	Data uintptr    //指向数组的指针
	Len  int        //切片长度
	Cap  int        //切片容量
}

slice自动扩容机制

  1. 如果期望容量大于当前容量的两倍就会使用期望容量;
  2. 如果当前切片容量小于 1024 就会将容量翻倍;
  3. 如果当前切片容量大于 1024 就会每次增加 25% 的容量,直到新容量大于期望容量;

坑点

  1. Slice并没有提供官方的函数可供查询某元素是否存在,所以只能手动遍历Slice;
  2. 在使用Slice时如果没有预估容量,可能会带来很大的扩容开销,如果是在高并发场景下,一定要记得预估capacity并提前预创建
  3. 删除一个Slice中的元素是一件比较麻烦的事情
  4. Slice赋值为浅拷贝,这一点特别值得注意。在没有扩容的情况下,复制后的切片和原始切片指向的数组将是同一个,操作两者都会操作同一个底层数组,并改变自己的len和cap,而另一个切片的结构信息不会改变;有扩容的情况下,复制后的切片会指向一个新的数组,gc不会将原来的数组清理掉。
Slice赋值问题
package main
import "fmt"

func main() {
        data := make(map[string][]int32)
        item0 := make([]int32, 0)
        item0 = append(item0, 1)
        item0 = append(item0, 2)
        data["item0"] = item0

        // 获取到item0, 并向其中添加两项
        temp := data["item0"]
        temp = append(temp, 3)
        temp = append(temp, 4)

        // 输出结果是:temp len is 4, data["item0"] len is 2
        // 这里返回的temp,是一个data["item0"]的副本,内部指向同一个底层数组
        //其实此时他们已经指向不同的底层数组了,因为原始的cap只有2(可以推断),再append元素将自动扩容到4,temp也会指向不同的地址;其实很容易想到,在这一点上可能存在一些隐患
        fmt.Printf("temp len is %v, data[\"item0\"] len is %v\n", len(temp),  len(data["item0"]))

}

你可能感兴趣的:(Golang,golang,slice)