Go语言中:使用copy处理切片是否总是比append更高效?
拼接大切片:append可变参数 vs make+copy的效率对比
你的想法是正确的——append(sl, sl2...)在拼接两个大切片的场景下,效率和make+copy的组合几乎一致,甚至在部分场景下表现更优,完全可以放心用它来保证代码可读性。
为什么append不会多次扩容?
Go语言的append函数在处理append(s, s2...)这种形式时,会直接获取s2的长度,计算出需要的总元素数:len(s) + len(s2)。接下来的内存分配逻辑是:
- 如果原切片
s的剩余容量(cap(s) - len(s))足够容纳s2的所有元素,就直接在原切片的底层数组后追加数据,不需要分配新内存; - 如果剩余容量不够,会一次性分配刚好满足总长度的内存(或按Go的扩容策略分配,但不会分多次扩容),然后把原切片和
s2的数据一次性复制到新数组中。
和make+copy的本质对比
make+copy的逻辑是预先分配刚好大小的内存,然后分两次把两个切片的数据复制进去。而append在需要扩容时,底层做的是同样的事:一次性分配足够内存,然后批量复制原切片和传入切片的数据。两者的内存操作和复制次数完全一致,性能差异可以忽略不计。
如果原切片本身还有剩余容量,append甚至会更高效——因为它不需要创建新的切片,直接复用原底层数组,而make+copy无论如何都会新建一个切片。
基准测试验证
你可以通过Go的基准测试直观看到两者的性能差异:
func BenchmarkAppendSlice(b *testing.B) { sl := make([]int, 10000) sl2 := make([]int, 10000) b.ResetTimer() for i := 0; i < b.N; i++ { _ = append(sl, sl2...) } } func BenchmarkMakeCopySlice(b *testing.B) { sl := make([]int, 10000) sl2 := make([]int, 10000) b.ResetTimer() for i := 0; i < b.N; i++ { nsl := make([]int, len(sl)+len(sl2)) copy(nsl, sl) copy(nsl[len(sl):], sl2) _ = nsl } }
运行测试后会发现,两者的耗时几乎持平,在原切片有剩余容量的情况下,append的表现还会略好。
总结
在拼接两个大切片时,append(sl, sl2...)既保证了代码的可读性,又不会带来额外的性能开销,你的判断完全正确——它只会进行一次扩容(如果需要的话),效率和手动make+copy相当。
内容的提问来源于stack exchange,提问作者jcquokka88
相关产品推荐
相关产品推荐

