Loading...
墨滴

caspar

2021/10/08  阅读:23  主题:凝夜紫

深入理解slice

基本概念

slice的底层结构是一个结构体,无固定长度的数组,包含如下3个属性

type slice struct {
 array unsafe.Pointer // 包含了一个指向一个数组的指针,数据实际上存储在这个指针指向的数组上
 len   int // 当前 slice 使用到的长度
 cap   int // 当前 slice 的容量,同时也是底层数组 array 的长度
}

常用操作

初始化

s1 := make([]int, 2)
fmt.Printf("s1(%p): %v, len: %d, cap: %d\n", &s1, s1, len(s1), cap(s1))

增加

s1[0] = 1
s1[1] = 2
s1 = append(s1, 3) // 完成了扩容
fmt.Printf("s1(%p): %v, len: %d, cap: %d\n", &s1, s1, len(s1), cap(s1))

删除

s1 = append(s1[:1], s1[2:]...)
fmt.Printf("s1(%p): %v, len: %d, cap: %d\n", &s1, s1, len(s1), cap(s1))

修改

s1[0] = 5

查找

v := s1[0]

通过 append 之后 s1 的容量和长度都发生了变化,说明完成了自动扩容

删除元素之后我们的长度发生了变化,但是容量还是原本不变

扩容机制

  1. 如果需要的最小容量比两倍原有容量大,那么就取需要的容量

  2. 如果原有 slice 长度小于 1024 那么每次就扩容为原来的两倍

  3. 如果原 slice 大于等于 1024 那么每次扩容就扩为原来的 1.25 倍

注意事项

// 初始化slice
s1 := []int{1,2,3,4,5}

// 复制一个 slice
s2 := s1
fmt.Printf("s2(%p): %v, len: %d, cap: %d\n", &s2, s2, len(s2), cap(s2))

// 修改slice1(不会扩容)
s1[0] = 10 // 这里可以发现,s1[0] s2[0] 都被修改为了 10
fmt.Printf("s1(%p): %v, len: %d, cap: %d\n", &s1, s1, len(s1), cap(s1))
fmt.Printf("s2(%p): %v, len: %d, cap: %d\n", &s2, s2, len(s2), cap(s2))

// 修改slice1(会扩容)
s1 = append(s1, 5, 6, 7, 8)
s1[0] = 11 // 这里可以发现,s1[0] 被修改为了 11, s2[0] 还是10
fmt.Printf("s1(%p): %v, len: %d, cap: %d\n", &s1, s1, len(s1), cap(s1))
fmt.Printf("s2(%p): %v, len: %d, cap: %d\n", &s2, s2, len(s2), cap(s2))
  • 扩容前,修改 s1 的元素之后,s2 的元素也被修改了

  • 扩容后,s2 的元素就不会随着 s1 的修改而变化了

在复制 slice 的时候,slice 中数组的指针也被复制了,在触发扩容逻辑之前,两个 slice 指向的是相同的数组,触发扩容逻辑之后指向的就是不同的数组了

caspar

2021/10/08  阅读:23  主题:凝夜紫

作者介绍

caspar