Golang - slice切片如何扩容(示例)
深入解析扩容原理(查看这里)
内部结构
type slice struct {
array unsafe.Pointer
len int
cap int
}
扩容机制
规则1
- 当需要的容量大于原切片容量的两倍时,会使用需要的容量作为新容量;
- 当原切片长度小于1024时,新切片的容量会直接翻倍;
- 而当原切片的容量大于等于1024时,会反复地增加25%,也就是变为原来容量的1.25倍,直到新容量超过所需要的容量。
规则2
- 如果扩容之后还没有超过原数组的容量,那切片中的指针指向的位置还是原数组;
- 如果扩容之后超过了原数组的容量,那就会开辟一块新的内存,把原来的值拷贝过来,这种情况丝毫不会影响到原数组。
示例
规则1
1、当小于1024个元素时
func main() {
// 建立容量为2的切片
addCap := make([]string, 0, 2)
fmt.Println("addCap 第0次:", addCap, ",长度为:", len(addCap), ",容量为:", cap(addCap), ",地址为:", &addCap)
// 插入一个数,占一个容量
addCap = append(addCap, "1")
// 打印此时的地址
fmt.Println("addCap 第1次:", addCap, ",长度为:", len(addCap), ",容量为:", cap(addCap), ",地址为:", &addCap[0])
// 插入一个数,占一个容量,再打印此时的地址
addCap = append(addCap, "2")
fmt.Println("addCap 第2次:", addCap, ",长度为:", len(addCap), ",容量为:", cap(addCap), ",地址为:", &addCap[0])
// 插入一个数,占一个容量,再打印此时的地址
addCap = append(addCap, "3")
// 此时三个数已经超出容量,那么切片容量将扩容(变为2倍),此时地址也将变成新的地址
fmt.Println("addCap 第3次:", addCap, ",长度为:", len(addCap), ",容量为:", cap(addCap), ",地址为:", &addCap[0])
}
2、当大于1024个元素时
func main() {
// 创建容量为1022的切片
addCap := make([]int, 1022, 1024)
fmt.Println("addCap 第0次", "长度为:", len(addCap), ",容量为:", cap(addCap), ",地址为", &addCap[0])
// 插入一个数,占一个容量 容量 1023
addCap = append(addCap, 1)
// 打印此时的地址
fmt.Println("addCap 第1次", "长度为:", len(addCap), ",容量为:", cap(addCap), ",地址为", &addCap[0])
// 插入一个数,占一个容量,再打印此时的地址
addCap = append(addCap, 2)
fmt.Println("addCap 第2次", "长度为:", len(addCap), ",容量为:", cap(addCap), ",地址为", &addCap[0])
// 插入一个数,占一个容量,再打印此时的地址
addCap = append(addCap, 3)
// 此时已经超出容量1024,那么切片容量将扩容(变为1.25倍),此时地址也将变成新的地址
fmt.Println("addCap 第3次", "长度为:", len(addCap), ",容量为:", cap(addCap), ",地址为", &addCap[0])
}
规则2
简单理解内存地址更换
func main() {
// 建立容量为2的切片
addCap := make([]string, 0, 2)
// 插入一个数,占一个容量
addCap = append(addCap, "1")
// 打印此时的地址
fmt.Println("addCap 第1次:", addCap, ",容量为", cap(addCap), ",指针为", &addCap[0])
// 插入一个数,占一个容量,再打印此时的地址
addCap = append(addCap, "2")
fmt.Println("addCap 第2次:", addCap, ",容量为", cap(addCap), ",指针为", &addCap[0])
// 将other的指针指向切片地址
// 再打印此时的地址 和 addCap 一样,即未触及容量时,还是原数组
other := addCap[0:1]
fmt.Println("other 第1次:", other, ",容量为", cap(other), ",指针为", &other[0])
//此时修改原数组 oth 所指向的地址不变,但第一个数的值已经更改 3
addCap[0] = "3"
fmt.Println("other 第2次:", other, ",容量为", cap(other), ",指针为", &other[0])
// 插入一个数,占一个容量,再打印此时的地址
addCap = append(addCap, "5")
// 此时再修改 已经是扩容后的新地址 原数组将保持不变 即other所指向的地址的值不变
addCap[0] = "4"
// 此时三个数已经超出容量,那么切片容量将扩容,此时地址也将变成新的地址,第一个数也将修改为4
fmt.Println("addCap 第3次:", addCap, ",容量为", cap(addCap), ",指针为", &addCap[0])
// 但 other 依然保留着原数组的指针地址,所以依然还是3
fmt.Println("other 第3次:", other, ",容量为", cap(other), ",指针为", &other[0])
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」