本文摘自php中文网,作者藏色散人,侵删。
每次 append 操作都会检查 slice 是否有足够的容量,如果足够会直接在原始数组上追加元素并返回一个新的 slice,底层数组不变
而若容量不够,会创建一个新的容量足够的底层数组,先将之前数组的元素复制过来,再将新元素追加到后面,然后返回新的 slice,底层数组改变
而这里对新数组的容量定义是按 乘以2
的机制增加
相关教程推荐:《golang教程》
而今天看到关于 Golang 切片的底层结构即 reflect.SliceHeader 时,发现 append 的扩容并不完全是2倍增长,源码如下(Go version 1.13):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | func grow(s Value, extra int) (Value, int, int) {
i0 := s.Len()
i1 := i0 + extra
if i1 < i0 {
panic( "reflect.Append: slice overflow" )
}
m := s.Cap()
if i1 <= m {
return s.Slice(0, i1), i0, i1
}
if m == 0 {
m = extra
} else {
for m < i1 {
if i0 < 1024 {
m += m
} else {
m += m / 4
}
}
}
t := MakeSlice(s.Type(), i1, m)
Copy (t, s)
return t, i0, i1
}
func Append(s Value, x ...Value) Value {
s.mustBe(Slice)
s, i0, i1 := grow(s, len(x))
for i, j := i0, 0; i < i1; i, j = i+1, j+1 {
s.Index(i).Set(x[j])
}
return s
}
|
首先 Append 判断类型是否 slice,然后调用 grow 扩容,从 l1 <= m 的判断可以发现确实容量足够的情况下,只是对原始数组建立一个新的 slice
但当容量不足时,可以看到只有在当前元素 i0 小于1024时,才是按2倍速度正常,否则其实每次只增长25%,代码验证如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 | func main() {
str := make([]int, 1023)
fmt.Println(len(str), cap(str))
str = append(str, 1)
fmt.Println(len(str), cap(str))
str = append(str, 1)
fmt.Println(len(str), cap(str))
}
输出:
1023 1023
1024 2048
1025 2048
|
初始容量已经达到1024后,只增长了256
1 2 3 4 5 6 7 8 9 10 11 12 | func main() {
str := make([]int, 1024)
fmt.Println(len(str), cap(str))
str = append(str, 1)
fmt.Println(len(str), cap(str))
str = append(str, 1)
fmt.Println(len(str), cap(str))
}
输出:
1024 1024
1025 1280
1026 1280
|
当然这里还有个疑惑在于,初始容量为1023时,并不是按1023×2,而是直接1024×2,测试初始500扩容也是直接按512×2,猜测更底层在动态调整时总会补充为2的n次方,目前builtin包下只看到 append 的定义说明,还有待继续挖掘
以上就是关于Golang Slice的append扩容的详细内容,更多文章请关注木庄网络博客!!
相关阅读 >>
聊聊dubbo-go-proxy的jtypes
go - 实现项目内链路追踪(二)
使用viper读取nacos配置(开源)
golang判断interface为nil
golang 为什么还有指针
模块一 go语言基础知识-库源码文件
手撸golang 仿spring ioc/aop 之8 扫码3
go写的第一个小程序记账系统,来啦
javascript 和 golang 前后端使用 aes 加密传输数据
windows10下编译go项目为linux可执行文件
更多相关阅读请进入《golang》频道 >>
老貘
一个与时俱进的Go编程知识库。
转载请注明出处:木庄网络博客 » 关于Golang Slice的append扩容