`copy()`でスライスを複製できないのはなぜですか?


121

Goでスライスのコピーを作成する必要があります。ドキュメントを読むと、コピー機能が自由に使えます。

copy組み込み関数は、ソーススライスから宛先スライスに要素をコピーします。(特殊なケースとして、文字列からバイトのスライスにバイトをコピーします。)ソースと宛先がオーバーラップする場合があります。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]

遊び場で確認できます。では、なぜスライスをコピーできないのですか?


みんなありがとう、スライスが同じ長さであることに気づかなかったのは本当に悲しいです。
サルバドールダリ2015年

1
必ずしも同じである必要はありませんが、dstコピーする要素と少なくとも同じ大きさにする必要があります(完全なコピーsrcとはlen(dst) >= len(src))。
icza

2
b := append([]int{}, a...)
ロケットスペーサー2017年

回答:


209

ビルトインは要素をcopy(dst, src)コピーしmin(len(dst), len(src))ます。

したがって、dst空の場合(len(dst) == 0)、何もコピーされません。

tmp := make([]int, len(arr))プレイグラウンドに移動)を試してください:

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言語仕様:スライスへの追加とコピーに記載されています。

コピーされる要素の数は、len(src)およびの最小値ですlen(dst)

編集:

最後にのドキュメントcopy()が更新され、ソースと宛先の最小長がコピーされるという事実が含まれています。

Copyは、コピーされた要素の数を返します。これは、len(src)とlen(dst)の最小値になります。


2
要約copyすると、宛先スライスが小さすぎる場合、宛先スライスを拡大するためのロジックは含まれていませんが、別の組み込み関数があります。append この例では、最初に適切なサイズのスライスを割り当てることをお勧めしますが、appendすでにスライスがあり、最後に要素を追加してそれを拡大したい場合に使用できます。
thomasrutter 2017

1
しかし、無制限のサイズのスライスをコピーするときに、制限されたサイズのスライスを作成する必要があるのはなぜですか?
アレックス、

24

これを行うもう1つの簡単な方法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


8
ここでの問題は、実際の例でははるかに大きいため、追加の処理によって追加のメモリが割り当てられない限り、追加の処理によって余分なメモリが割り当てられることになります。これは、繰り返しの呼び出しで効率的に再割り当てできるように設計されているためです。play.golang.org/p/5_6618xnXnは、cap(x)が10ではなく12に増加することを確認します。1つの値を1048576の値に追加するとどうなるかを確認します。play.golang.org/ p / nz32JPehhl容量は2048スロット増加して、 1050624、1つの追加値のみに対応します。
j。アンドリューシュスタ

12

あなたのスライスが同じサイズだったらそれはうまくいくでしょう

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

10

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()を使用してすべての要素を1行で初期化し、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]

要素数が1,000を超える場合は、allocation + copy()と比較して、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

1
appendは、これを見越して楽観的に過剰な容量を割り当てるため、繰り返しの呼び出しによって配列が増加する場合に使用する必要があります。結果の配列を正確なサイズに作成する必要があり、再配置されない場合は、入力配列ごとに1回コピーを使用する必要があります。play.golang.org/p/0kviwKmGzxこれらの結果を生成したベンチマークコードを共有しなかったため、その有効性を確認または否定できませんが、このより重要な側面を見落としています。
j。アンドリューシュスタ

1
配列ではなく「スライス」を意味します。彼らは違うものです。
イナンクムス

2

Goプログラミング言語仕様

スライスへの追加とコピー

関数copyは、スライス要素をソースsrcから宛先dstにコピーし、コピーされた要素の数を返します。両方の引数は、同じ要素タイプTでなければならず、タイプ[] Tのスライスに割り当て可能でなければなりません。コピーされる要素の数は、len(src)とlen(dst)の最小値です。特殊なケースとして、copyは、文字列型のソース引数を持つ[] 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]

0

これはスライスをコピーする方法です。少し遅れますが、@ Daveよりも簡単で高速な答えがあります。これは、@ Daveのようなコードから生成された命令です。これらは私のものです。ご覧のとおり、手順ははるかに少なくなっています。何をするかというと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]
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.