2015-05-12 28 views
40

我需要做一个切片的副本去阅读文档有一个copy函数在我的处置。为什么我不能在golang中使用`copy()`来复制切片?

复制内置函数将源片段中的元素复制到 目标片段中。 (作为特殊情况,它也会将 字符串中的字节复制到一段字节中。)源和目标可能会重叠。 复制返回复制的元素数量,这将是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 。那么为什么我不能复制一个切片?

+0

谢谢大家,真的很伤心,我没有注意到,切片应该是相同的长度。 –

+1

不一定相同,但是'dst'应该至少与你想复制的许多元素一样大(对于'src'的完整副本,这意味着'len(dst)> = len(src)')。 – icza

+0

'b:= append([] int {},a ...)' – rocketspacer

回答

90

的内置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)的最小值的

+6

Lol ...通常的嫌疑人。 +1 – VonC

+1

他们真的应该记录它......花费相当长的时间搞清楚...... – Bob

+0

@Bob我刚刚检查和文档已更新,以包含此信息。我也更新了答案,提到这一点。 – icza

9

如果你的片大小相同的,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”:

复制功能支持片之间进行复制不同的长度(它只会复制到较小的数字元件)的

通常的例子是:

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

The Go Programming Language Specification

Appending to and copying slices

功能拷贝拷贝切片从源SRC为 目的地DST元件和返回复制的元素的数量。两个参数 必须具有相同的元素类型T,并且必须可分配给 类型为[] T的片段。复制元素的数量是最小的 len(src)和len(dst)。作为一种特殊情况,copy还接受一个 目标参数,该参数可分配给类型为[]字节的字符串类型的源参数 。该表格将字符串中的字节复制到 字节片段中。

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] 
6

另一种简单的方法是使用append,它将在过程中分配片。

arr := []int{1, 2, 3} 
tmp := []int{} 
tmp = append(tmp, arr...) // Notice the ... splat 
fmt.Println(tmp) 
fmt.Println(arr) 

输出(如预期):

[1 2 3] 
[1 2 3] 

因此,对于复制阵列arr的速记将tmp := append([]int{}, arr...)

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

+0

这里的问题是,在现实世界的例子中,append会分配多余的内存 - 除非这个数组后来被一些进一步的处理填充到容量 - 因为它被设计为通过重复调用进行有效的重新分配。 https://play.golang.org/p/5_6618xnXn观察cap(x)增加到12,而不是10.现在看看当1值被添加到1048576值时会发生什么https://play.golang.org/p/ nz32JPehhl容量跳转2048个插槽到1050624,只容纳一个附加值。 –

3

副本()运行的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] 

可以初始化并使用附加()将一个零切片复制所有元件在一行中。

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] 

与分配+拷贝(),对于大于1,000的元件,使用附加的比较。事实上,低于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 
+0

追加应该用于数组将因重复调用而增加的情况,因为它会乐观地分配过剩的容量以预期这一点。在结果数组应该按照确切大小创建并且不再被重新分配的情况下,每个输入数组应该使用一次副本。 https://play.golang.org/p/0kviwKmGzx您没有分享产生这些结果的基准代码,所以我无法确认或否认其有效性,但忽略了这一更重要的方面。 –

相关问题