在Go中连接两个切片

我正在尝试组合切片[1, 2]和切片[3, 4]。如何在Go中执行此操作?

我试过:

append([]int{1,2}, []int{3,4})

但得到了:

cannot use []int literal (type []int) as type int in append

然而,留档似乎表明这是可能的,我错过了什么?

slice = append(slice, anotherSlice...)
514111 次浏览

在第二个切片后添加点:

//                           vvv
append([]int{1,2}, []int{3,4}...)

这就像任何其他可变参数函数一样。

func foo(is ...int) {
for i := 0; i < len(is); i++ {
fmt.Println(is[i])
}
}


func main() {
foo([]int{9,8,7,6,5}...)
}

附加和复制切片

变量函数append将零个或多个值x附加到s 类型S,它必须是切片类型,并返回结果 切片,也属于S类型。值x被传递给 类型...T其中TS的元素类型 参数传递规则适用。作为特例,append也接受 第一个参数可分配给类型[]byte,第二个参数为 string类型后跟...。此表单追加 字符串。

append(s S, x ...T) S  // T is the element type of S


s0 := []int{0, 0}
s1 := append(s0, 2)        // append a single element     s1 == []int{0, 0, 2}
s2 := append(s1, 3, 5, 7)  // append multiple elements    s2 == []int{0, 0, 2, 3, 5, 7}
s3 := append(s2, s0...)    // append a slice              s3 == []int{0, 0, 2, 3, 5, 7, 0, 0}

将参数传递给…参数

如果f是最终参数类型...T的可变参数,则在 函数参数等价于[]T类型的参数。在 每次调用f,传递给最终参数的参数都是一个新的参数 类型[]T的切片,其连续元素是实际参数, 所有这些都必须可分配给类型T。切片的长度为 因此绑定到最终参数的参数数和可能 不同的调用站点

您问题的答案是Go编程语言规范中的示例s3 := append(s2, s0...)。例如,

s := append([]int{1, 2}, []int{3, 4}...)

没有反对其他答案,但我发现的文档中的简短解释比其中的示例更容易理解:

func append

func append(slice []Type, elems ...Type) []Type内置的追加 函数将元素附加到切片的末尾。如果它有足够的 容量,目的地被重新切片以容纳新元素。 如果没有,将分配一个新的底层数组。附加 返回更新的切片。因此,有必要存储 append的结果,通常在持有切片本身的变量中:

slice = append(slice, elem1, elem2)
slice = append(slice, anotherSlice...)

作为特殊情况,将字符串附加到字节切片是合法的, 像这样:

slice = append([]byte("hello "), "world"...)

我认为重要的是要指出并知道,如果目标切片(您附加到的切片)具有足够的容量,则附加将通过重新切片目标(重新切片到增加其长度以便能够容纳可附加元素)来“就地”发生。

这意味着如果目标是通过切片更大的数组或切片创建的,该数组或切片具有超出结果切片长度的其他元素,则它们可能会被覆盖。

要演示,请参阅此示例:

a := [10]int{1, 2}
fmt.Printf("a: %v\n", a)


x, y := a[:2], []int{3, 4}
fmt.Printf("x: %v, y: %v\n", x, y)
fmt.Printf("cap(x): %v\n", cap(x))


x = append(x, y...)
fmt.Printf("x: %v\n", x)


fmt.Printf("a: %v\n", a)

输出(在Go Playground上尝试):

a: [1 2 0 0 0 0 0 0 0 0]
x: [1 2], y: [3 4]
cap(x): 10
x: [1 2 3 4]
a: [1 2 3 4 0 0 0 0 0 0]

我们创建了一个长度为10的“支持”数组a。然后我们通过切片这个a数组创建x目标切片,y切片是使用复合文字[]int{3, 4}创建的。现在,当我们将y附加到x时,结果是预期的[1 2 3 4],但可能令人惊讶的是,支持数组a也发生了变化,因为x的容量是10,足以将y附加到它,因此x被重新切片,它也将使用相同的a支持数组,105将y的元素复制到那里。

如果您想避免这种情况,您可以使用全切片表达式,其形式为

a[low : high : max]

它构造一个切片,并通过将其设置为max - low来控制结果切片的容量。

查看修改后的示例(唯一的区别是我们像这样创建xx = a[:2:2]

a := [10]int{1, 2}
fmt.Printf("a: %v\n", a)


x, y := a[:2:2], []int{3, 4}
fmt.Printf("x: %v, y: %v\n", x, y)
fmt.Printf("cap(x): %v\n", cap(x))


x = append(x, y...)
fmt.Printf("x: %v\n", x)


fmt.Printf("a: %v\n", a)

输出(在Go Playground上尝试)

a: [1 2 0 0 0 0 0 0 0 0]
x: [1 2], y: [3 4]
cap(x): 2
x: [1 2 3 4]
a: [1 2 0 0 0 0 0 0 0 0]

如你所见,我们得到了相同的x结果,但支持数组a没有改变,因为x的容量“只有”2(感谢完整的切片表达式a[:2:2])。因此,为了进行追加,分配了一个新的支持数组,可以存储xy的元素,这与a不同。

append([]int{1,2}, []int{3,4}...)将工作。将参数传递给...参数。

如果f是可变参数,其最终参数p的类型为...T,则在f中,p的类型等价于[]T的类型。

如果调用f时没有p的实际参数,则传递给p的值为nil

否则,传递的值是一个类型为[]T的新切片,带有一个新的底层数组,其连续元素是实际参数,这些参数都必须可分配给T。因此,切片的长度和容量是绑定到p的参数数,并且可能因每个调用站点而异。

给定函数和调用

func Greeting(prefix string, who ...string)
Greeting("nobody")
Greeting("hello:", "Joe", "Anna", "Eileen")

append()函数和扩展运算符

两个切片可以使用标准Go语言库中的append方法连接。这类似于variadic函数操作。所以我们需要使用...

package main


import (
"fmt"
)


func main() {
x := []int{1, 2, 3}
y := []int{4, 5, 6}
z := append([]int{}, append(x, y...)...)
fmt.Println(z)
}

上面代码的输出是:[1 2 3 4 5 6]

我想强调@icza的答案并简化它,因为它是一个至关重要的概念。我假设读者熟悉切片

c := append(a, b...)

这是对问题的正确回答。 但是如果你需要在不同的上下文中稍后在代码中使用切片'a'和'c',这不是连接切片的安全方法。

为了解释,让我们不要以切片的形式读取表达式,而是以底层数组的形式读取表达式:

"获取(底层)'a'数组并将数组'b'中的元素附加到 它。如果数组“a”有足够的容量来包含“b”中的所有元素 -'c'的底层数组不会是一个新数组,它实际上是数组'a'。基本上,切片'a'将显示 底层数组'a'和切片'c'将显示数组'a'的len(c)。”

append()不一定会创建一个新数组!这可能会导致意想不到的结果。请参阅Go Playground示例

如果您想确保为切片分配新数组,请始终使用make()函数。例如,这里有一些丑陋但足够高效的任务选项。

la := len(a)
c := make([]int, la, la + len(b))
_ = copy(c, a)
c = append(c, b...)

la := len(a)
c := make([]int, la + len(b))
_ = copy(c, a)
_ = copy(c[la:], b)

要连接两个切片,

func main() {
s1 := []int{1, 2, 3}
s2 := []int{99, 100}
s1 = append(s1, s2...)


fmt.Println(s1) // [1 2 3 99 100]
}

将单个值附加到切片

func main() {
s1 :=  []int{1,2,3}
s1 := append(s1, 4)
    

fmt.Println(s1) // [1 2 3 4]
}

将多个值附加到切片

func main() {
s1 :=  []int{1,2,3}
s1 = append(s1, 4, 5)
    

fmt.Println(s1) // [1 2 3 4]
}

似乎是泛型的完美用法(如果使用1.18或更高版本)。

func concat[T any](first []T, second []T) []T {
n := len(first);
return append(first[:n:n], second...);
}