Go语言切片的扩容机制
- 初始创建:当创建一个切片时,例如
make([]int, 0, 10)
,这里容量(cap)被初始化为10。如果没有指定容量,会默认根据初始元素个数来分配容量。
- 扩容时机:当向切片中添加元素(如使用
append
函数),导致元素个数超过当前容量时,就会触发扩容。
- 扩容策略:
- 如果当前切片的容量小于1024,新的容量会翻倍。例如,原容量为5,添加元素触发扩容后,新容量变为10。
- 如果当前切片的容量大于或等于1024,新的容量会增加当前容量的1/4。比如,原容量为1024,扩容后新容量为1024 + 1024/4 = 1280。
- 扩容后会重新分配内存,将原切片的数据复制到新的内存地址。
性能优化方法
- 预分配容量:在实际开发中,如果能够预估切片最终的元素数量,在创建切片时就通过
make
函数指定合适的容量。例如,已知最终会有1000个元素,可以 make([]int, 0, 1000)
,这样可以避免多次扩容带来的内存重新分配和数据复制开销。
- 减少不必要的扩容:避免在循环中频繁添加单个元素导致多次扩容。可以先将元素收集到一个临时切片中,当临时切片满了再批量添加到目标切片。例如:
var targetSlice []int
tempSlice := make([]int, 0, 100)
for i := 0; i < 1000; i++ {
tempSlice = append(tempSlice, i)
if len(tempSlice) == cap(tempSlice) {
targetSlice = append(targetSlice, tempSlice...)
tempSlice = make([]int, 0, cap(tempSlice))
}
}
if len(tempSlice) > 0 {
targetSlice = append(targetSlice, tempSlice...)
}
- 复用切片:在函数调用等场景下,如果可能,尽量复用已有的切片,避免重新创建新切片带来的容量分配和数据复制。例如,函数接收一个切片参数,在函数内对切片进行操作,返回时尽量复用该切片而不是创建新的。
func processSlice(slice []int) []int {
// 对切片进行操作
return slice
}