2015-05-12 57 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您沒有分享產生這些結果的基準代碼,所以我無法確認或否認其有效性,但忽略了這一更重要的方面。 –

相關問題