在项目中看到前辈们留下的代码,其中有一段代码如下
package main
import (
"fmt"
)
func main() {
invalidA := []string{"1", "2"}
invalidB := []string{"3", "4"}
invalidC := []string{"5", "6"}
// sample code
invalid := make([]string, 0)
invalid = append(invalid, invalidA...)
invalid = append(invalid, invalidB...)
invalid = append(invalid, invalidC...)
fmt.Println(invalid)
}
// Output:[1 2 3 4 5 6]
这段代码功能没有问题,但不够优雅,同时会触发多次 Slice 扩容,性能不够好。
在重构代码之前,先看一个合并数组容易忽略的问题。
package main
import (
"fmt"
)
func main() {
s1 := make([]int, 2, 5)
s1[0], s1[1] = 2, 3
s2 := []int{7, 8}
s3 := append(s1, s2...)
fmt.Println(s1, s3)
s3[0] = -1
fmt.Println(s1, s3)
fmt.Println(s1[:4])
}
输出如下
[2 3] [2 3 7 8]
[-1 3] [-1 3 7 8]
[-1 3 7 8]
本例中,如果 s1 的容量够用,那么 append
会修改 s1,然后将 s1 的地址赋值给 s3,对 s3 的修改其实就是在修改 s1
这大概率不符合预期。
如何避免呢?可以通过 Slice 表达式将 s1 的长度和容量修改为 s1 元素的数量。
s3 = append(s1[:len(s1):len(s1)], s2...)
输出如下,可以看到对 s3 的修改已经不会影响到 s1,append
在追加数据时发现 s1 的容量不够,新申请了一块儿内存。
[2 3] [2 3 7 8]
[2 3] [-1 3 7 8]
[2 3 0 0]
回到最初的代码,append
支持可变参数的追加,但并不支持追加多个 Slices
可以封装两个函数,一个函数处理两个 Slices 的合并以及多个 Slices 合并。
concatSlice.go
func concatSlice[T any](first []T, second []T) []T {
n := len(first)
return append(first[:n:n], second...)
}
concatMultipleSlices.go
func concatMultipleSlices[T any](slices [][]T) []T {
var totalLen int
for _, s := range slices {
totalLen += len(s)
}
result := make([]T, totalLen)
var i int
for _, s := range slices {
i += copy(result[i:], s)
}
return result
}
借助封装好的泛型函数,最初的示例可以修改如下,代码结构及性能都有相应提升。
package main
import (
"fmt"
)
func main() {
invalidA := []string{"1", "2"}
invalidB := []string{"3", "4"}
invalidC := []string{"5", "6"}
// sample code
invalid := concatMultipleSlices([][]string{invalidA, invalidB, invalidC})
fmt.Println(invalid)
}
// Output: [1 2 3 4 5 6]
参考