为什么我不能在Golang中复制带有`copy()`的切片?

时间:2015-05-12 05:33:27

标签: go slice

我需要在Go中复制一个切片并阅读文档,我可以使用copy函数。

  

复制内置函数将元素从源切片复制到   目的地切片。 (作为一种特殊情况,它也会从a复制字节   字符串到一片字节。)源和目标可能重叠。   复制返回复制的元素数,这将是最小值   len(src)和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上查看。那么为什么我不能复制片?

7 个答案:

答案 0 :(得分:146)

内置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包中没有记录,但Go Language Specification: Appending to and copying slices中记录了它:

  

复制的元素数量是len(src)len(dst)的最小值。

修改

最后,copy()的文档已经更新,现在它包含了源和目标的最小长度将被复制的事实:

  

复制返回复制的元素数,即len(src)和len(dst)的最小

答案 1 :(得分:13)

另一种简单的方法是使用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]

因此复制数组arr的简写将是append([]int(nil), arr...)

https://play.golang.org/p/sr_4ofs5GW

<强>更新

要不分配多余的内存,您可以事先设置所需的容量:

arr := []int{1, 2, 3}
tmp := make([]int, 0, len(arr))
tmp = append(tmp, arr...)
fmt.Println(len(tmp)) # 3
fmt.Println(cap(tmp)) # 3

https://play.golang.org/p/aIQCrvVXyTq

答案 2 :(得分:11)

如果您的切片大小相同,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]

来自&#34; Go Slices: usage and internals&#34;:

  

复制功能支持在不同长度的切片之间进行复制(它只会复制到较少数量的元素

通常的例子是:

t := make([]byte, len(s), (cap(s)+1)*2)
copy(t, s)
s = t

答案 3 :(得分:8)

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()将所有元素初始化并复制到一行中。

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()相比,对于超过1,000个元素,请使用append。实际上低于1000这个差异可能会被忽略,除非你有很多切片,否则请将其作为经验法则。

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

答案 4 :(得分:2)

  

The Go Programming Language Specification

     

Appending to and copying slices

     

函数副本将切片元素从源src复制到   destination dst并返回复制的元素数。都   参数必须具有相同的元素类型T并且必须可分配给   一片[] T型。复制的元素数量是最小值   len(src)和len(dst)。作为一个特例,副本也接受一个   目标参数可赋值为带有source参数的type [] byte   字符串类型。此表单将字符串中的字节复制到   字节切片。

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]

答案 5 :(得分:0)

注意:这是@benlemasurier证明的错误解决方案

这是一种复制切片的方法。我来晚了一点,但是有一个比@Dave更快,更简单的答案。 This是从类似@Dave的代码生成的指令。 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]

答案 6 :(得分:0)

克隆为切片的最佳方法是

sClone = append(s[:0:0], s...)

这种实现有两个优点:

  1. 如果s为nil,则确保结果sClone为nil,并且不为nil 如果 s 不为零。

  2. 即使 T 在 另一个包