在切片长度小于1024时会扩容为原来的2倍,超过1024扩容为原来的1.25倍if cap > doublecap内存对齐
func growslice(et *_type, old slice, cap int) slice {
// ……
newcap := old.cap
doublecap := newcap + newcap
if cap > doublecap {
newcap = cap
} else {
if old.len < 1024 {
newcap = doublecap
} else {
for newcap < cap {
newcap += newcap / 4
}
}
}
// ……
//内存对齐操作
capmem = roundupsize(uintptr(newcap) * ptrSize)
newcap = int(capmem / ptrSize)
}
看两个例子吧:
例1:
func main() {
s := []int{1,2}
s = append(s,4)
s = append(s,5)
s = append(s,6)
fmt.Printf("len=%d, cap=%d",len(s),cap(s))
}
len=5, cap=8
例2
func main() {
s := []int{1,2}
s = append(s,4,5,6)
fmt.Printf("len=%d, cap=%d",len(s),cap(s))
}
len=5, cap=8
growslice
满足源码中第一个 if 条件6
len=5, cap=6
例3:
func main() {
s := []int{1,2,3,4,5}
s = append(s,6,7)
fmt.Printf("len=%d, cap=%d",len(s),cap(s))
}
len=7, cap=10
懵逼了没?崩溃了没?
为了避免困惑,我们来总结一下吧:
slice扩容机制:
>
A = append(A,B...)
newcap:=roundupsize(len(A)+len(B))
新旧长度之和,即最终slice长度
<=
最容易忽视的的细节,再强调一遍:
slice在append时,必须关注最终slice的长度是否超过原容量的2倍
- 如果不是,那么就按最开始的结论。
- 如果是,则容量就变为新slice的容量。
最后,执行内存对齐操作,这一步是一定不能少的!
0、1、2、4、6、8、10、12、14、16、18、20 ....
最最后,来个小练习:
2+9=1112
func main() {
s := []int{1,2}
s = append(s,1,2,3,4,5,6,7,8,9)
fmt.Printf("len=%d, cap=%d",len(s),cap(s)) //len=11, cap=12
}
- 扩容为2倍,正好能放下
func main() {
s := []int{1,2}
s = append(s,3,4)
fmt.Printf("len=%d, cap=%d",len(s),cap(s)) //len=4, cap=4
}
- 扩容为2倍,能放下而且还有空余
func main() {
s := []int{1,2,3}
s = append(s,4,5)
fmt.Printf("len=%d, cap=%d",len(s),cap(s)) //len=5, cap=6 ,
}
- 新切片长度为0,容量不为0,不进行扩容
func main() {
s := []int{1,2,3}
s1 := make([]int,0,4)//len=0 cap=4
s = append(s,s1...)
fmt.Printf("len=%d, cap=%d",len(s),cap(s)) // len=3, cap=3,
}
5.新切片长度不为0时
func main() {
s := []int{1,2,3}
s1 := make([]int,4,4)//len=0 cap=4
s = append(s,s1...)
fmt.Printf("len=%d, cap=%d",len(s),cap(s)) //len=7, cap=8
}
示例4、5说明是按被追加切片的长度(而不是容量)来计算扩容的。
以上示例是针对长度小于1024的情况,大于1024的情况同样适用。