在 Go 语言中 , 有一个很常用的数据结构 , 那就是切片(Slice) 。
【Go 语言切片是如何扩容的?】切片是一个拥有相同类型元素的可变长度的序列 , 它是基于数组类型做的一层封装 。它非常灵活 , 支持自动扩容 。
切片是一种引用类型 , 它有三个属性:指针 , 长度和容量 。
文章插图
底层源码定义如下:
type slice struct {array unsafe.Pointerlenintcapint}
- 指针: 指向 slice 可以访问到的第一个元素 。
- 长度: slice 中元素个数 。
- 容量: slice 起始元素到底层数组最后一个元素间的元素个数 。
文章插图
声明和初始化切片的使用还是比较简单的 , 这里举一个例子 , 直接看代码吧 。
func main() {var nums []int// 声明切片fmt.Println(len(nums), cap(nums)) // 0 0nums = Append(nums, 1)// 初始化fmt.Println(len(nums), cap(nums)) // 1 1nums1 := []int{1,2,3,4}// 声明并初始化fmt.Println(len(nums1), cap(nums1))// 4 4nums2 := make([]int,3,5)// 使用make()函数构造切片fmt.Println(len(nums2), cap(nums2))// 3 5}
扩容时机当切片的长度超过其容量时 , 切片会自动扩容 。这通常发生在使用 append 函数向切片中添加元素时 。扩容时 , Go 运行时会分配一个新的底层数组 , 并将原始切片中的元素复制到新数组中 。然后 , 原始切片将指向新数组 , 并更新其长度和容量 。
需要注意的是 , 由于扩容会分配新数组并复制元素 , 因此可能会影响性能 。如果你知道要添加多少元素 , 可以使用 make 函数预先分配足够大的切片来避免频繁扩容 。
接下来看看 append 函数 , 签名如下:
func Append(slice []int, items ...int) []int
append 函数参数长度可变 , 可以追加多个值 , 还可以直接追加一个切片 。使用起来比较简单 , 分别看两个例子:追加多个值:
package mainimport "fmt"func main() {s := []int{1, 2, 3}fmt.Println("初始切片:", s)s = append(s, 4, 5, 6)fmt.Println("追加多个值后的切片:", s)}
输出结果为:
初始切片: [1 2 3]追加多个值后的切片: [1 2 3 4 5 6]
再来看一下直接追加一个切片:
package mainimport "fmt"func main() {s1 := []int{1, 2, 3}fmt.Println("初始切片:", s1)s2 := []int{4, 5, 6}s1 = append(s1, s2...)fmt.Println("追加另一个切片后的切片:", s1)}
输出结果为:
初始切片: [1 2 3]追加另一个切片后的切片: [1 2 3 4 5 6]
再来看一个发生扩容的例子:
package mainimport "fmt"func main() {s := make([]int, 0, 3) // 创建一个长度为0 , 容量为3的切片fmt.Printf("初始状态: len=%d cap=%d %vn", len(s), cap(s), s)for i := 1; i <= 5; i++ {s = append(s, i) // 向切片中添加元素fmt.Printf("添加元素%d: len=%d cap=%d %vn", i, len(s), cap(s), s)}}
输出结果为:
初始状态: len=0 cap=3 []添加元素1: len=1 cap=3 [1]添加元素2: len=2 cap=3 [1 2]添加元素3: len=3 cap=3 [1 2 3]添加元素4: len=4 cap=6 [1 2 3 4]添加元素5: len=5 cap=6 [1 2 3 4 5]
在这个例子中 , 我们创建了一个长度为 0? , 容量为 3? 的切片 。然后 , 我们使用 append? 函数向切片中添加 5 个元素 。
当我们添加第 4? 个元素时 , 切片的长度超过了其容量 。此时 , 切片会自动扩容 。新的容量是原始容量的两倍 , 即 6 。
表面现象已经看到了 , 接下来 , 我们就深入到源码层面 , 看看切片的扩容机制到底是什么样的 。
源码分析在 Go 语言的源码中 , 切片扩容通常是在进行切片的 append? 操作时触发的 。在进行 append? 操作时 , 如果切片容量不足以容纳新的元素 , 就需要对切片进行扩容 , 此时就会调用 growslice 函数进行扩容 。
推荐阅读
- HTTPS是如何保证密文不能被篡改的?
- 让很多人直呼“要失业”,火爆网络的AIGC,到底是什么东西?
- “流动型程序员”指的是什么类型的程序员?
- 张继科|张继科欠不欠巨额赌债我不知道但张继科非常缺钱我倒是早就看出来
- 景甜|乱套了!那些买卖景甜视频的人,你们是疯了吗?
- |万能钓饵的正确用法,渔获差异巨大,原因是少了几个步骤
- 辅警|大量政府员工给辞退!主要是哪些岗位人员?
- 80年代|一盘棋总是有胜负的
- 薏仁红豆芡实水
- 清热消暑糖水