在Go中连接两个切片

Kev*_*rke 411 append variadic-functions go slice

我正在尝试将切片[1, 2]和切片组合在一起[3, 4].我怎么能在Go中这样做?

我试过了:

append([]int{1,2}, []int{3,4})
Run Code Online (Sandbox Code Playgroud)

但得到了:

cannot use []int literal (type []int) as type int in append
Run Code Online (Sandbox Code Playgroud)

但是,文档似乎表明这是可能的,我错过了什么?

slice = append(slice, anotherSlice...)
Run Code Online (Sandbox Code Playgroud)

小智 738

在第二个切片后添加点:

//---------------------------vvv
append([]int{1,2}, []int{3,4}...)
Run Code Online (Sandbox Code Playgroud)

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

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

func main() {
    foo([]int{9,8,7,6,5}...)
}
Run Code Online (Sandbox Code Playgroud)

  • `append()`一个可变参数函数,```允许你从一个切片向一个可变参数函数传递多个参数. (34认同)
  • @Toad:它实际上并没有将它们分散开来.在上面的`foo()`示例中,`is`参数保存原始切片的副本,也就是说它具有对相同底层数组len和cap的轻量级引用的副本.如果`foo`函数改变了一个成员,则会在原始文件中看到更改.[这是一个演示](http://play.golang.org/p/JDLgYcMFFN).所以唯一真正的开销就是它创建一个新的切片,如果你还没有,例如:`foo(1,2,3,4,5)`这将创建一个新的切片,```将保持. (13认同)
  • 当切片很大时,这是否完全符合要求?或者编译器是否真的没有将所有元素作为参数传递? (9认同)
  • 啊.如果我理解正确,可变参数函数实际上就像一个参数数组(而不是堆栈中的每个参数)实现?因为你传入切片,它实际上是一对一映射的? (2认同)

pet*_*rSO 72

附加和复制切片

可变参数函数向 类型append附加零个或多个值,该值必须是切片类型,并返回结果切片,也是类型.这些值将传递给type的参数,其中元素类型为,并且相应的参数传递规则适用.作为一种特殊情况,append还接受第一个可赋值给type 的参数,其中第二个参数 类型后跟.此表单附加字符串的字节.xsSSx...TTS[]bytestring...

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}
Run Code Online (Sandbox Code Playgroud)

将参数传递给...参数

如果f是具有最终参数类型的可变参数...T,则在函数内该参数等效于类型的参数[]T.在每次调用时f,传递给final参数的参数是一个新的切片类型,[]T其连续元素是实际参数,所有参数都必须可分配给该类型T.因此,切片的长度是绑定到最终参数的参数的数量,并且对于每个呼叫站点可以不同.

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

s := append([]int{1, 2}, []int{3, 4}...)
Run Code Online (Sandbox Code Playgroud)

  • @Hugo如果你"覆盖"数组的一部分,那么知道切片"所有者"将能够看到/覆盖超出切片当前长度的数组部分.如果您不想这样,可以使用[完整切片表达式](https://golang.org/ref/spec#Slice_expressions)(以"a [low:high:max]"的形式)指定最大_capacity_.例如,切片"a [0:2:4]"将具有"4"的容量,并且不能将其复制为包含超出该数量的元素,即使支持数组之后具有一千个元素也是如此. (7认同)
  • 注意:一般使用append(slice1,slice2 ......)对我来说似乎很危险.如果slice1是较大数组的切片,则该数组的值将被slice2覆盖.(这让我感到畏缩,这似乎不是一个普遍关注的问题?) (6认同)

fia*_*jaf 27

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

func追加

func append(slice []Type, elems ...Type) []Type附加内置函数将元素附加到切片的末尾.如果它具有足够的容量,则会复制目标以容纳新元素.如果没有,将分配新的底层数组.Append返回更新的切片.因此有必要存储append的结果,通常在保存切片本身的变量中:

slice = append(slice, elem1, elem2)
slice = append(slice, anotherSlice...)
Run Code Online (Sandbox Code Playgroud)

作为一种特殊情况,将字符串附加到字节切片是合法的,如下所示:

slice = append([]byte("hello "), "world"...)
Run Code Online (Sandbox Code Playgroud)

  • 谢谢!对我来说很有价值! (2认同)

D.C*_*Joo 23

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

c := append(a, b...)
Run Code Online (Sandbox Code Playgroud)

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

为了解释一下,让我们不是根据切片,而是根据底层数组来阅读表达式:

“取(底层)'a' 数组并将数组 'b' 中的元素附加到它。如果数组 'a' 有足够的容量来包含来自 'b' 的所有元素 - 'c' 的底层数组将不是一个新数组,它实际上是数组 'a'。基本上,切片 'a' 将显示底层数组 'a' 的 len(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...)
Run Code Online (Sandbox Code Playgroud)
la := len(a)
c := make([]int, la + len(b))
_ = copy(c, a)
_ = copy(c[la:], b)
Run Code Online (Sandbox Code Playgroud)


icz*_*cza 22

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

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

要演示,请参阅此示例:

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)
Run Code Online (Sandbox Code Playgroud)

输出(在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]
Run Code Online (Sandbox Code Playgroud)

我们创建了一个a长度为"支持"的数组10.然后我们x通过切割此a数组来创建目标切片,y使用复合文字创建切片[]int{3, 4}.现在,当我们追加yx,结果是预期的[1 2 3 4],但什么可奇怪的是,支持数组a也改变了,因为容量x10这足以追加y到它,所以x是resliced这也将使用相同的a支持数组,并append()将复制元素复制y到那里.

如果要避免这种情况,可以使用具有该表单的完整切片表达式

a[low : high : max]
Run Code Online (Sandbox Code Playgroud)

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

查看修改后的示例(唯一的区别是我们创建x如下x = 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)
Run Code Online (Sandbox Code Playgroud)

输出(在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]
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,我们得到了相同的x结果,但支持数组a没有改变,因为容量x是"仅" 2(由于完整的切片表达式a[:2:2]).这样做追加,一个新的背衬阵列被分配,可以同时存储的元素xy,这是从不同a.

  • 这对我面临的问题非常有帮助。谢谢。 (2认同)

ASH*_*EEV 9

append() 函数和扩展运算符

可以使用append标准 golang 库中的方法连接两个切片。类似于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)
}
Run Code Online (Sandbox Code Playgroud)

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

  • 我不知道为什么你不使用 `z :=append(x, y...)`。 (5认同)

Gan*_*thy 7

要连接两个切片,

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

    fmt.Println(s1) // [1 2 3 99 100]
}
Run Code Online (Sandbox Code Playgroud)

将单个值附加到切片

func main() {
    s1 :=  []int{1,2,3}
    s1 := append(s1, 4)
    
    fmt.Println(s1) // [1 2 3 4]
}
Run Code Online (Sandbox Code Playgroud)

将多个值附加到切片

func main() {
    s1 :=  []int{1,2,3}
    s1 = append(s1, 4, 5)
    
    fmt.Println(s1) // [1 2 3 4]
}
Run Code Online (Sandbox Code Playgroud)


Pet*_*erM 5

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

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

  • append 已经是“通用的”,所以人们可能会认为这不是类型参数的必要用例,**但是**三索引切片表达式`:n:n`的非明显用法来削减容量第一片是一个明显的进步 (2认同)