我想要把切片 [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...)
我想要把切片 [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...)
// 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}...)
}
Appending to and copying slices
The variadic function
append
appends zero or more valuesx
tos
of typeS
, which must be a slice type, and returns the resulting slice, also of typeS
. The valuesx
are passed to a parameter of type...T
whereT
is the element type ofS
and the respective parameter passing rules apply. As a special case, append also accepts a first argument assignable to type[]byte
with a second argument ofstring
type followed by...
. This form appends the bytes of the 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}
Passing arguments to ... parameters
If
f
is variadic with final parameter type...T
, then within the function the argument is equivalent to a parameter of type[]T
. At each call off
, the argument passed to the final parameter is a new slice of type[]T
whose successive elements are the actual arguments, which all must be assignable to the typeT
. The length of the slice is therefore the number of arguments bound to the final parameter and may differ for each call site.
你的问题的答案可以在Go编程语言规范中找到,例如s3 := append(s2, s0...)
。举个例子,
s := append([]int{1, 2}, []int{3, 4}...)
a[low:high:max]
),它还指定了最大容量。例如,切片a[0:2:4]
的容量为4
,即使支持该切片的数组在此之后有一千个元素,也不能重新切片以包括超过它的元素。 - icza我想强调@icza的答案并稍微简化一下,因为这是一个关键的概念。我假设读者已经熟悉了切片。
c := append(a, b...)
这是对问题的有效回答。 但是,如果您需要在稍后的不同上下文中使用'slices' a'和'c',那么这不是连接'slices'的安全方式。
为了说明,让我们不用'slices'的术语来阅读表达式,而是用底层数组的术语:
"取(基础)数组'a'并将元素从数组'b'附加到其中。如果数组'a'具有足够的容量,以包括来自'b'的所有元素-基础数组'c'将不是一个新数组,它实际上将是数组'a'。基本上,'slice' 'a'将显示长度为a的元素底层数组'a',而'slice' 'c'将显示长度为c的数组'a'的元素。"
append()不一定会创建新数组!这可能会导致意外的结果。请参见 Go Playground示例 。
如果要确保为'slice'分配新数组,请始终使用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...)
- Chris不是针对其他答案,但我发现在文档中提供的简要解释比其中的示例更易于理解:
func append
func append(slice []Type, elems ...Type) []Type
The append built-in function appends elements to the end of a slice. If it has sufficient capacity, the destination is resliced to accommodate the new elements. If it does not, a new underlying array will be allocated. Append returns the updated slice. It is therefore necessary to store the result of append, often in the variable holding the slice itself:
slice = append(slice, elem1, elem2) slice = append(slice, anotherSlice...)
As a special case, it is legal to append a string to a byte slice, like this:
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的"backing"数组a
,然后通过对这个a
数组进行切片来创建x
目标切片,使用复合字面量[]int{3, 4}
来创建y
切片。现在当我们将y
追加到x
时,结果是期望的[1 2 3 4]
,但令人惊讶的是,背后的数组a
也发生了变化,因为x
的容量是10
,足以将y
追加到其中,所以x
会被重新切片,并且也会使用相同的a
作为背后的数组,append()
函数会将y
中的元素复制到那里。a[low : high : max]
通过设置 max - low
,构建一个切片并控制结果切片的容量。
请参阅修改后的示例(唯一的区别是我们像这样创建 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
的长度为20,那么a
是否会保持不变? - patrickappend()
会分配一个新的支持数组,将旧内容复制到其中,并在新的支持数组上执行追加操作,同时保留旧数组不变。尝试一下有多难呢?Go Playground - icza使用标准golang库中的append
方法,可以将两个切片连接起来。这类似于可变参数
函数操作。因此我们需要使用...
。
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)
}
z := append(x, y...)
。 - user12817546连接两个切片,
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]
}
func concat[T any](first []T, second []T) []T {
n := len(first);
return append(first[:n:n], second...);
}
:n:n
来减少第一个切片的容量的非显而易见的用法确实是一种改进。 - blackgreenappend([]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")
func Concat[S ~[]E, E any](slices ...S) S
泛型函数。// Join slices into a new slice
a := []int{ 1, 2, 3 }
b := []int{ 4, 5, 6 }
c := slices.Concat(nil, a, b)
// c == int{ 1, 2, 3, 4, 5, 6 }
s := [][]int{{1}, nil, {2}}
c = slices.Concat(s...)
// c == int{1, 2}
append()
是一个可变参数函数,而...
则允许你从切片中传递多个参数给可变参数函数。 - user1106925foo()
示例中,is
参数保存原始切片的副本,也就是说它具有对相同底层数组、长度和容量的轻量引用的副本。如果foo
函数改变了一个成员,更改将在原始数据上被看到。这里有一个演示。因此,唯一真正的开销是当你没有一个切片时,它会创建一个新的切片,比如:foo(1, 2, 3, 4, 5)
,它将创建一个新的切片,is
将持有它。 - user1106925...
时,它只是传递该切片。当你传递单个参数时,它会将它们收集到一个新的切片中并传递它。我没有第一手的了解确切的机制,但我猜测这样做:foo(1, 2, 3, 4, 5)
和这样做:func foo(is ...int) {
只是简化为这样做:foo([]int{1, 2, 3, 4, 5})
和这样做:func foo(is []int) {
。 - user1106925