Go: 切片(Slice)

Slice

创建切片

数组与切片的区别

  • 数组是值类型,编译时确定大小且拷贝时拷贝值,可以通过[...]type{val1, val2}编译器自动推断长度
  • 切片是引用类型,切片必须基于一个数组获得,对切片的操作会影响数组本身,因此也会影响同样共享该数组的切片

切割

  • 数组切割是一个语法糖,类似其它语言的切片语法,返回切片类型

  • 话虽如此,go的切片比较简单,只支持自然数索引:

    1
    2
    3
    4
    ns := [...]int{1, 2, 3, 4}
    ns[1:] // [1, 4)
    ns[:] // [0, 4)
    ns[:len(ns)-1] // [0, 3)
  • 切片分割:与数组分割类似,数组和切片在默认时容量都是底层数组的长度减去start

  • 更常用的是完整的start:end:max表达式来防止切片影响原切片:

    1
    2
    3
    4
    5
    6
    ns := [...]int{1, 2, 3, 4}
    ns1 := ns[:]
    // 指定容量为 max-start, 一般指定 max=end 即可
    ns2 := ns1[1:2:2]
    // 如果不指定容量则因没有爆容量而影响原切片
    append(ns2, 1, 2)

使用make()预分配空间

  • 直接使用[]type创建无法预先指定其底层数组的空间

  • 使用make()是常用的方法:

    1
    ns := make([]int, len, cap)

    len表示已经确认存在的值,并初始化为零值,cap为底层数组的容量

  • 实际上lencap是切片的基本组成属性

自动扩容

  • slice本身会自动扩容,重新分配到新的内存并指向它

  • 因此当基于数组/切片分割获取切片时,需要注意,在没有重新分配时对切片进行修改会影响原数组,在拼接后如果超出容量则不会影响原数组

    最好的习惯是使用完整的start:end:cap

切片转数组与解包

  • Go 1.17支持[LEN](s[:LEN])长度匹配的转换
  • Go 1.20支持[LEN](s)省略切片符,要求len(s)>=LEN
  • 转换行为在内存上创建新的数组副本
  • 切片能通过后跟...解包,通常也用于作为可变参数传递

切片常用操作

内置函数

  • len(slice) int:获取长度
  • cap(slice) int:获取容量
  • append(slice []Type, elems ... Type):拼接切片,返回新切片对象并共用底层数组,不改变原切片
  • copy(dst []Type, src []Type) int:拷贝数组,返回实际复制的元素个数,即min(len(dts), len(src))

衍生操作

  • 删除指定元素:s = append(s[:i], s[i+1:]...)
  • 删除连续元素:s = append(s[:start], s[end:]...)
  • 插入指定元素:s = append(s[:i], append([]T{v}, s[i:]...)...)

slices

  • Go 1.21时推出了slices标准库,具有大量切片常用操作,且和strings内的大部分操作相似,也包括排序函数和迭代器函数