提问人:Kevin Burke 提问时间:4/27/2013 最后编辑:iczaKevin Burke 更新时间:11/23/2022 访问量:596408
在 Go 中连接两个切片
Concatenate two slices in Go
问:
我正在尝试将切片和切片结合起来.我怎样才能在 Go 中做到这一点?[1, 2]
[3, 4]
我试过了:
append([]int{1,2}, []int{3,4})
但得到了:
cannot use []int literal (type []int) as type int in append
但是,文档似乎表明这是可能的,我错过了什么?
slice = append(slice, anotherSlice...)
答:
在第二个切片后添加点:
// 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()
一个可变参数函数,并且允许您将多个参数从切片传递给可变参数函数。...
foo()
is
foo
foo(1, 2, 3, 4, 5)
is
...
foo(1, 2, 3, 4, 5)
func foo(is ...int) {
foo([]int{1, 2, 3, 4, 5})
func foo(is []int) {
可变参数函数将零个或多个值追加到 的类型 中,该类型必须是切片类型,并返回结果 切片,也是 .这些值将传递给 type 其中 是 的元素类型,并且各自 参数传递规则适用。作为特例,append 也接受 第一个参数可分配给类型,第二个参数为 type,后跟 。此表单附加 字符串。
append
x
s
S
S
x
...T
T
S
[]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}
如果 是具有最终参数类型的可变参数,则在 函数的参数等价于类型的参数。在 的每次调用 ,传递给 final 参数的参数都是一个新的 类型的切片,其连续元素是实际参数, 其中所有都必须可分配给类型 。切片的长度为 因此,绑定到最终参数的参数数和 每个呼叫站点都不同。
f
...T
[]T
f
[]T
T
您的问题的答案是 Go 编程语言规范中的示例。例如s3 := append(s2, s0...)
s := append([]int{1, 2}, []int{3, 4}...)
评论
a[low : high : max]
a[0:2:4]
4
不反对其他答案,但我发现文档中的简要解释比其中的示例更容易理解:
函数追加
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]
我们创建了一个长度为 的“后备”数组。然后我们通过切片这个数组来创建目标切片,切片是使用复合文字创建的。现在当我们附加到 时,结果是预期的,但可能令人惊讶的是,后备数组也发生了变化,因为 的容量足以附加到它,所以被切片,它也将使用相同的后备数组,并将 的元素复制到那里。a
10
x
a
y
[]int{3, 4}
y
x
[1 2 3 4]
a
x
10
y
x
a
append()
y
如果要避免这种情况,可以使用具有
a[low : high : max]
它构造一个切片,并通过将结果切片设置为 来控制其容量。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)
输出(在 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]
x
y
a
评论
y
a
append()
append([]int{1,2}, []int{3,4}...)
会起作用。将参数传递给参数。...
如果 是 variadic 的,其最终参数为 type ,则在 的 type 中等价于 type 。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( ) 函数和扩展运算符
可以使用标准 golang 库中的 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]
评论
z := append(x, y...)
我想强调@icza答案并稍微简化一下,因为它是一个至关重要的概念。我假设读者熟悉切片。
c := append(a, b...)
这是对这个问题的有效回答。但是,如果您需要稍后在不同的上下文中使用切片“a”和“c”,则这不是连接切片的安全方法。
为了解释,让我们不要从切片的角度来阅读表达式,而是从基础数组的角度来理解:
“获取(基础)'a'数组,并将数组'b'中的元素附加到 它。如果数组 'a' 有足够的容量来包含 'b' 中的所有元素 - “C”的底层数组不会是一个新数组,它实际上是数组“A”。基本上,切片 'a' 将显示 len(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)
评论
a := append(a, 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...);
}
评论
:n:n
评论