为什么我不能用 `copy()` 复制切片?
Why can't I duplicate a slice with `copy()`?
我需要在 Go 中复制一个切片并阅读文档,其中有一个 copy 函数供我使用。
The copy built-in function copies elements from a source slice into a
destination slice. (As a special case, it also will copy bytes from a
string to a slice of bytes.) The source and destination may overlap.
Copy returns the number of elements copied, which will be the minimum
of len(src) and len(dst).
但是当我这样做时:
arr := []int{1, 2, 3}
tmp := []int{}
copy(tmp, arr)
fmt.Println(tmp)
fmt.Println(arr)
我的tmp
和以前一样是空的(我什至尝试使用arr, tmp
):
[]
[1 2 3]
您可以随时查看 playground。那么为什么我不能复制一个切片呢?
如果你的切片大小相同,it would work:
arr := []int{1, 2, 3}
tmp := []int{0, 0, 0}
i := copy(tmp, arr)
fmt.Println(i)
fmt.Println(tmp)
fmt.Println(arr)
会给:
3
[1 2 3]
[1 2 3]
来自“Go Slices: usage and internals”:
The copy function supports copying between slices of different lengths (it will copy only up to the smaller number of elements)
通常的例子是:
t := make([]byte, len(s), (cap(s)+1)*2)
copy(t, s)
s = t
The Go Programming Language Specification
Appending to and copying slices
The function copy copies slice elements from a source src to a
destination dst and returns the number of elements copied. Both
arguments must have identical element type T and must be assignable to
a slice of type []T. The number of elements copied is the minimum of
len(src) and len(dst). As a special case, copy also accepts a
destination argument assignable to type []byte with a source argument
of a string type. This form copies the bytes from the string into the
byte slice.
copy(dst, src []T) int
copy(dst []byte, src string) int
tmp
需要足够的空间容纳 arr
。例如,
package main
import "fmt"
func main() {
arr := []int{1, 2, 3}
tmp := make([]int, len(arr))
copy(tmp, arr)
fmt.Println(tmp)
fmt.Println(arr)
}
输出:
[1 2 3]
[1 2 3]
内置 copy(dst, src)
复制 min(len(dst), len(src))
个元素。
因此,如果您的 dst
为空 (len(dst) == 0
),则不会复制任何内容。
尝试 tmp := make([]int, len(arr))
(Go Playground):
arr := []int{1, 2, 3}
tmp := make([]int, len(arr))
copy(tmp, arr)
fmt.Println(tmp)
fmt.Println(arr)
输出(如预期):
[1 2 3]
[1 2 3]
不幸的是,builtin
package, but it is documented in the Go Language Specification: Appending to and copying slices:
中没有记录
The number of elements copied is the minimum of len(src)
and len(dst)
.
编辑:
终于更新了 copy()
的文档,它现在包含将复制源和目标的最小长度的事实:
Copy returns the number of elements copied, which will be the minimum of len(src) and len(dst).
另一种简单的方法是使用 append
,它将在进程中分配切片。
arr := []int{1, 2, 3}
tmp := append([]int(nil), arr...) // Notice the ... splat
fmt.Println(tmp)
fmt.Println(arr)
输出(如预期):
[1 2 3]
[1 2 3]
正如下面的评论中所指出的,append
如果开始时切片的大小不正确,则可能会分配过多的内存。一个很好的解决方案是预分配正确容量的一部分,如下所示:
tmp := append(make([]int, 0, len(arr)), arr...)
所以用于复制数组 arr
的 shorthand 将是 append(make([]int, 0, len(arr)), arr...)
copy()运行的dst和src的长度最短,所以你必须将dst初始化为你想要的长度。
A := []int{1, 2, 3}
B := make([]int, 3)
copy(B, A)
C := make([]int, 2)
copy(C, A)
fmt.Println(A, B, C)
输出:
[1 2 3] [1 2 3] [1 2]
您可以使用 append() 将一行中的所有元素初始化并复制到 nil 切片。
x := append([]T{}, []...)
示例:
A := []int{1, 2, 3}
B := append([]int{}, A...)
C := append([]int{}, A[:2]...)
fmt.Println(A, B, C)
输出:
[1 2 3] [1 2 3] [1 2]
对比allocation+copy(),大于1000个元素,使用append。实际上低于 1,000 的差异可能会被忽略,除非您有很多切片,否则请根据经验进行操作。
BenchmarkCopy1-4 50000000 27.0 ns/op
BenchmarkCopy10-4 30000000 53.3 ns/op
BenchmarkCopy100-4 10000000 229 ns/op
BenchmarkCopy1000-4 1000000 1942 ns/op
BenchmarkCopy10000-4 100000 18009 ns/op
BenchmarkCopy100000-4 10000 220113 ns/op
BenchmarkCopy1000000-4 1000 2028157 ns/op
BenchmarkCopy10000000-4 100 15323924 ns/op
BenchmarkCopy100000000-4 1 1200488116 ns/op
BenchmarkAppend1-4 50000000 34.2 ns/op
BenchmarkAppend10-4 20000000 60.0 ns/op
BenchmarkAppend100-4 5000000 240 ns/op
BenchmarkAppend1000-4 1000000 1832 ns/op
BenchmarkAppend10000-4 100000 13378 ns/op
BenchmarkAppend100000-4 10000 142397 ns/op
BenchmarkAppend1000000-4 2000 1053891 ns/op
BenchmarkAppend10000000-4 200 9500541 ns/op
BenchmarkAppend100000000-4 20 176361861 ns/op
NOTE: This is an incorrect solution as @benlemasurier proved
这是复制切片的一种方法。我来晚了一点,但有一个比@Dave 更简单、更快的答案。 This are the instructions generated from a code like @Dave's. These是我生成的指令。如您所见,指令要少得多。它所做的只是复制切片的 append(slice)
。此代码:
package main
import "fmt"
func main() {
var foo = []int{1, 2, 3, 4, 5}
fmt.Println("foo:", foo)
var bar = append(foo)
fmt.Println("bar:", bar)
bar = append(bar, 6)
fmt.Println("foo after:", foo)
fmt.Println("bar after:", bar)
}
输出这个:
foo: [1 2 3 4 5]
bar: [1 2 3 4 5]
foo after: [1 2 3 4 5]
bar after: [1 2 3 4 5 6]
克隆为切片的最佳方法是
sClone = append(s[:0:0], s...)
这个实现有两个优点:
如果 s 为 nil 且不为 nil,请确保结果 sClone 为 nil
如果 s 不为零。
即使在中声明了T,也无需导入类型T的包含包
另一个包裹
甜美、简单、高效、无需注意长度、无内存重叠、不同副本
slice2 := append([]int{}, slice1...)
如果你不关心速度:
import "golang.org/x/exp/slices"
tmp := slices.Clone(arr)
使用 Go 1.18 和泛型,现在可以使用包 "golang.org/x/exp/slices"
中的 slices.Clone
复制任何切片。 Playground
我需要在 Go 中复制一个切片并阅读文档,其中有一个 copy 函数供我使用。
The copy built-in function copies elements from a source slice into a destination slice. (As a special case, it also will copy bytes from a string to a slice of bytes.) The source and destination may overlap. Copy returns the number of elements copied, which will be the minimum of len(src) and len(dst).
但是当我这样做时:
arr := []int{1, 2, 3}
tmp := []int{}
copy(tmp, arr)
fmt.Println(tmp)
fmt.Println(arr)
我的tmp
和以前一样是空的(我什至尝试使用arr, tmp
):
[]
[1 2 3]
您可以随时查看 playground。那么为什么我不能复制一个切片呢?
如果你的切片大小相同,it would work:
arr := []int{1, 2, 3}
tmp := []int{0, 0, 0}
i := copy(tmp, arr)
fmt.Println(i)
fmt.Println(tmp)
fmt.Println(arr)
会给:
3
[1 2 3]
[1 2 3]
来自“Go Slices: usage and internals”:
The copy function supports copying between slices of different lengths (it will copy only up to the smaller number of elements)
通常的例子是:
t := make([]byte, len(s), (cap(s)+1)*2)
copy(t, s)
s = t
The Go Programming Language Specification
Appending to and copying slices
The function copy copies slice elements from a source src to a destination dst and returns the number of elements copied. Both arguments must have identical element type T and must be assignable to a slice of type []T. The number of elements copied is the minimum of len(src) and len(dst). As a special case, copy also accepts a destination argument assignable to type []byte with a source argument of a string type. This form copies the bytes from the string into the byte slice.
copy(dst, src []T) int copy(dst []byte, src string) int
tmp
需要足够的空间容纳 arr
。例如,
package main
import "fmt"
func main() {
arr := []int{1, 2, 3}
tmp := make([]int, len(arr))
copy(tmp, arr)
fmt.Println(tmp)
fmt.Println(arr)
}
输出:
[1 2 3]
[1 2 3]
内置 copy(dst, src)
复制 min(len(dst), len(src))
个元素。
因此,如果您的 dst
为空 (len(dst) == 0
),则不会复制任何内容。
尝试 tmp := make([]int, len(arr))
(Go Playground):
arr := []int{1, 2, 3}
tmp := make([]int, len(arr))
copy(tmp, arr)
fmt.Println(tmp)
fmt.Println(arr)
输出(如预期):
[1 2 3]
[1 2 3]
不幸的是,builtin
package, but it is documented in the Go Language Specification: Appending to and copying slices:
The number of elements copied is the minimum of
len(src)
andlen(dst)
.
编辑:
终于更新了 copy()
的文档,它现在包含将复制源和目标的最小长度的事实:
Copy returns the number of elements copied, which will be the minimum of len(src) and len(dst).
另一种简单的方法是使用 append
,它将在进程中分配切片。
arr := []int{1, 2, 3}
tmp := append([]int(nil), arr...) // Notice the ... splat
fmt.Println(tmp)
fmt.Println(arr)
输出(如预期):
[1 2 3]
[1 2 3]
正如下面的评论中所指出的,append
如果开始时切片的大小不正确,则可能会分配过多的内存。一个很好的解决方案是预分配正确容量的一部分,如下所示:
tmp := append(make([]int, 0, len(arr)), arr...)
所以用于复制数组 arr
的 shorthand 将是 append(make([]int, 0, len(arr)), arr...)
copy()运行的dst和src的长度最短,所以你必须将dst初始化为你想要的长度。
A := []int{1, 2, 3}
B := make([]int, 3)
copy(B, A)
C := make([]int, 2)
copy(C, A)
fmt.Println(A, B, C)
输出:
[1 2 3] [1 2 3] [1 2]
您可以使用 append() 将一行中的所有元素初始化并复制到 nil 切片。
x := append([]T{}, []...)
示例:
A := []int{1, 2, 3}
B := append([]int{}, A...)
C := append([]int{}, A[:2]...)
fmt.Println(A, B, C)
输出:
[1 2 3] [1 2 3] [1 2]
对比allocation+copy(),大于1000个元素,使用append。实际上低于 1,000 的差异可能会被忽略,除非您有很多切片,否则请根据经验进行操作。
BenchmarkCopy1-4 50000000 27.0 ns/op
BenchmarkCopy10-4 30000000 53.3 ns/op
BenchmarkCopy100-4 10000000 229 ns/op
BenchmarkCopy1000-4 1000000 1942 ns/op
BenchmarkCopy10000-4 100000 18009 ns/op
BenchmarkCopy100000-4 10000 220113 ns/op
BenchmarkCopy1000000-4 1000 2028157 ns/op
BenchmarkCopy10000000-4 100 15323924 ns/op
BenchmarkCopy100000000-4 1 1200488116 ns/op
BenchmarkAppend1-4 50000000 34.2 ns/op
BenchmarkAppend10-4 20000000 60.0 ns/op
BenchmarkAppend100-4 5000000 240 ns/op
BenchmarkAppend1000-4 1000000 1832 ns/op
BenchmarkAppend10000-4 100000 13378 ns/op
BenchmarkAppend100000-4 10000 142397 ns/op
BenchmarkAppend1000000-4 2000 1053891 ns/op
BenchmarkAppend10000000-4 200 9500541 ns/op
BenchmarkAppend100000000-4 20 176361861 ns/op
NOTE: This is an incorrect solution as @benlemasurier proved
这是复制切片的一种方法。我来晚了一点,但有一个比@Dave 更简单、更快的答案。 This are the instructions generated from a code like @Dave's. These是我生成的指令。如您所见,指令要少得多。它所做的只是复制切片的 append(slice)
。此代码:
package main
import "fmt"
func main() {
var foo = []int{1, 2, 3, 4, 5}
fmt.Println("foo:", foo)
var bar = append(foo)
fmt.Println("bar:", bar)
bar = append(bar, 6)
fmt.Println("foo after:", foo)
fmt.Println("bar after:", bar)
}
输出这个:
foo: [1 2 3 4 5]
bar: [1 2 3 4 5]
foo after: [1 2 3 4 5]
bar after: [1 2 3 4 5 6]
克隆为切片的最佳方法是
sClone = append(s[:0:0], s...)
这个实现有两个优点:
如果 s 为 nil 且不为 nil,请确保结果 sClone 为 nil 如果 s 不为零。
即使在中声明了T,也无需导入类型T的包含包 另一个包裹
甜美、简单、高效、无需注意长度、无内存重叠、不同副本
slice2 := append([]int{}, slice1...)
如果你不关心速度:
import "golang.org/x/exp/slices"
tmp := slices.Clone(arr)
使用 Go 1.18 和泛型,现在可以使用包 "golang.org/x/exp/slices"
中的 slices.Clone
复制任何切片。 Playground