Slice(切片)- 《Go 专家编程》笔记提要

切片(slice)

slice 又称动态数组,依托数组实现,可以方便地进行扩容和传递,实际使用时比数组更灵活。

初始化

  • 声明变量
    • 变量值都为零值,对于切片来讲零值为 nil
  • 字面量
    • 空切片是指长度为空,而值不是 nil
    • 声明长度为0的切片时,推荐使用变量声明的方式获得一个 nil 切片,而不是空切片,因为 nil 切片不需要内存分配
  • 内置函数make()
    • 推荐指定长度同时指定预估空间,可有效地减少切片扩容时内存分配及拷贝次数
  • 切取
    • 切片与原数组或切片共享底层空间
// 1. 声明变量
var s []int  // nil 切片

// 2. 字面量
s1 := []int{} // 空切片
s2 := []int{1, 2, 3} // 长度为3的切片

// 3. 内置函数 make() 
s3 := make([]int, 12) // 指定长度
s4 := make([]int, 12, 100) // 指定长度和空间

// 4. 切取
arr := [5]int{1, 2, 3, 4, 5}
s5 := arr[2:4] // 从数组中切取
s6 ;= s5[1:2] // 从切片中切取

操作

append()

当切片空间不足时,append() 会先创建新的大容量切片,添加元素后再返回新切片。

扩容规则

  • 原 slice 的容量小于 1024,则新 slice 的容量将扩大为原来的 2 倍
  • 原 slice 的容量大于或等于 1024,则新 slice 的容量将扩大为原来的 1.25 倍

append() 向 slice 添加一个元素的实现步骤如下:

  • 加入 slice 的容量够用,则将新元素追加进去,slice.len++,返回 slice
  • 原 slice 的容量不够,则将 slice 先扩容,扩容后得到新 slice
  • 将新元素追加进新 slice, slice.len++,返回新的 slice
s := make([]int, 0)
s = append(s, 1)  // 添加1个元素
s = append(s, 2, 3, 4 ,5) // 添加多个元素
s = append(s, []int{6, 7}...) //添加一个切片

len()cap()

由于切片的本质为结构体,结构体中存储了切片的长度和容量,所以这两个操作的时间复杂度均为 O(1)

copy()

会将源切片的数据逐个拷贝到目的切片指向的数组中,拷贝数量取两个切片长度的最小值。

例如长度为 10 的切片拷贝到长度为 5 的切片中时,将拷贝 5 个元素

也就是说,拷贝过程中不会发生扩容。

实现原理

src/runtime/slice.go:slice

type slice struct {
  array unsafe.Pointer
  len        int
  cap        int
}

array 指针指向底层数组,len 表示切片长度,cap 表示数组容量

切片表达式

  • 简单表达式 s[low : high]
  • 扩展表达式 s[low : high : max]

使用简单表达式生成的切片将与原数组或切片共享底层数组。新切片的生成逻辑可以使用一下伪代码表示:

b.array = &a[low]
b.len = high - low
b.cap = len(a) - low

// 扩展表达式中的 max 用于限制新生成切片的容量,新切片的容量为 max - low
b.cap = max - low

如果切片表达式发生越界就触发 panic

切取 string

作用于字符串时会则会产生新的字符串,而不是切片

扩展表达式不能用于切取 string

本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!