在Golang中连接多个切片

41

我正在尝试合并多个片段,如下所示,

package routes

import (
    "net/http"
)

type Route struct {
    Name        string
    Method      string
    Pattern     string
    Secured     bool
    HandlerFunc http.HandlerFunc
}

type Routes []Route

var ApplicationRoutes Routes

func init() {
    ApplicationRoutes = append(
        WifiUserRoutes,
        WifiUsageRoutes,
        WifiLocationRoutes,
        DashboardUserRoutes,
        DashoardAppRoutes,
        RadiusRoutes,
        AuthenticationRoutes...
    )
}

然而,内置的append()函数可以追加两个切片,因此在编译时会抛出too many arguments to append错误。是否有替代函数来实现这个任务?或者是否有更好的方法来合并这些切片?


Go 1.22 (Q1 2024) 的 slices.Concat 函数 应该会有所帮助。 - undefined
2个回答

44

这个问题已经有了答案,但我想在这里发帖是因为被接受的答案不是最有效的。

原因是创建一个空片并进行追加可能会导致许多不必要的分配。

最有效的方法是预先分配一个片并将元素复制到其中。以下是实现两种拼接方式的包。如果你进行基准测试,可以看到预分配大约快2倍,并且分配的内存要少得多。

基准测试结果:

go test . -bench=. -benchmem
testing: warning: no tests to run
BenchmarkConcatCopyPreAllocate-8    30000000            47.9 ns/op        64 B/op          1 allocs/op
BenchmarkConcatAppend-8             20000000           107 ns/op         112 B/op          3 allocs/op

拼接包:

package concat

func concatCopyPreAllocate(slices [][]byte) []byte {
    var totalLen int
    for _, s := range slices {
        totalLen += len(s)
    }
    tmp := make([]byte, totalLen)
    var i int
    for _, s := range slices {
        i += copy(tmp[i:], s)
    }
    return tmp
}

func concatAppend(slices [][]byte) []byte {
    var tmp []byte
    for _, s := range slices {
        tmp = append(tmp, s...)
    }
    return tmp
}

基准测试:

package concat

import "testing"

var slices = [][]byte{
    []byte("my first slice"),
    []byte("second slice"),
    []byte("third slice"),
    []byte("fourth slice"),
    []byte("fifth slice"),
}

var B []byte

func BenchmarkConcatCopyPreAllocate(b *testing.B) {
    for n := 0; n < b.N; n++ {
        B = concatCopyPreAllocate(slices)
    }
}

func BenchmarkConcatAppend(b *testing.B) {
    for n := 0; n < b.N; n++ {
        B = concatAppend(slices)
    }
}

2
https://blog.golang.org/slices 似乎也支持这种更高效的解决方案。 - jawang35

34

append 操作针对的是单个元素,而不是整个切片。在循环中逐个追加每个切片。

routes := []Routes{
    WifiUserRoutes,
    WifiUsageRoutes,
    WifiLocationRoutes,
    DashboardUserRoutes,
    DashoardAppRoutes,
    RadiusRoutes,
    AuthenticationRoutes,
}

var ApplicationRoutes []Route
for _, r := range routes {
    ApplicationRoutes = append(ApplicationRoutes, r...)
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接