

golangで使用するアレイの取り扱いについて質問させていただきます。

サンプルコードの後に、詳しい質問をさせていただきます。

[code]

    package main
    
    import "fmt"
    
    func main() {
    
        x := []int{1,2,3}
        y := x
    
        var z []int
        for _, v := range x {
            z = append(z, v)
        }
    
        x[1] = 4
        fmt.Println(x)
        fmt.Println(y)
        fmt.Println(z)
    }
    
[/code]

コードの中では、まずxというアレイを作成します。そのアレイを別の方法で２つのアレイ(y, z)にコピーしています。  
yはそのままコピーし、zはアレイの各要素をひとつづつコピーして作成します。

その後xの中身だけを変更し、すべてのアレイをPrintしているだけです。

質問ですが、  
なぜyアレイも同時に変更されてしまうのでしょうか？  
また、yのように作成したアレイもxとはシンクロさせない方法はあるのでしょうか？

ご教授お願いします。


CC BY-SA 3.0
2015-08-29T13:03:34.420
15003
2015-08-29T17:20:26.150
2015-08-29T17:20:26.150
11048
question
go
golangのアレイと取り扱いについて


`[]int{1,2,3}`はスライスリテラルと呼ばれ、配列ではなく配列を参照するスライスを作成します。`y`に`x`を代入した場合、配列はコピーされず`y`と`x`は同じ配列を指します。

[code]

    x := []int{1,2,3}    // 配列を作成してそのスライスを代入
    y := x               // スライスをコピー
                         // x, y の型は []int (スライス)
    
[/code]

配列を直接作成したい場合は、配列リテラル`[...]int{1,2,3}`または`[3]int{1,2,3}`を使用してください。

[code]

    x := [...]int{1,2,3} // 配列を作成
    y := x               // 配列をコピー
                         // x, y の型は [3]int (配列)
    
[/code]

参考: <http://blog.golang.org/go-slices-usage-and-internals>


CC BY-SA 3.0
2015-08-29T13:26:37.383
15006
2015-08-29T13:34:03.493
2015-08-29T13:34:03.493
3639
3639
15003
answer


質問では1次元配列を対象としていますが、コピーの対象が多次元配列の場合、`[...]` 表記(コンパイラに配列の要素数を計算させる)が使えない事があります。

例えば、以下の様な2次元配列を考えてみますと、

[code]

    x := [...][2]int{{1, 2}, {3, 4}, {5, 6}}
    y := x
    
[/code]

この場合は全ての要素がコピーされることになります。

しかし、以下の様に各要素の配列のサイズが異なる場合に、同じ要領で `[...][...]int` として定義するとコンパイルエラーになります。

[code]

    x := [...][...]int{{1, 2}, {3, 4, 5}, {6, 7, 8, 9}}
    
    use of [...] array outside of array literal
    
[/code]

そこで `[...][]int` としてしまうと、スライスの配列として定義されますので、スライスとしてコピーされる(shallow copy)事になります。

[code]

    x := [...][]int{{1, 2}, {3, 4, 5}, {6, 7, 8, 9}}
    y := x
    x[1][0] = 4
    =>
    x: [[1 2] [4 4 5] [6 7 8 9]]
    y: [[1 2] [4 4 5] [6 7 8 9]]
    
[/code]

要素の最大サイズを指定するという方法もありますが、

[code]

    x := [...][4]int{{1, 2}, {3, 4, 5}, {6, 7, 8, 9}}
    y := x
    x[1][0] = 4
    =>
    x: [[1 2 0 0] [4 4 5 0] [6 7 8 9]]
    y: [[1 2 0 0] [3 4 5 0] [6 7 8 9]]
    
[/code]

となってしまいます。

この様な多次元配列/スライスをコピー(deep copy)するには以下の方法があります。

  * deepcopy パッケージを使う

[mohae/deepcopy](https://github.com/mohae/deepcopy)

[code]

        x := [][]int{{1, 2}, {3, 4, 5}, {6, 7, 8, 9}}
        y := deepcopy.Iface(x).([][]int)
        x[1][0] = 4
        =>
        x: [[1 2] [4 4 5] [6 7 8 9]]
        y: [[1 2] [3 4 5] [6 7 8 9]]
    
[/code]

  * ループ処理で要素ごとにコピーする

[code]

        x := [][]int{{1, 2}, {3, 4, 5}, {6, 7, 8, 9}}
        y := make([][]int, len(x))
        for i, v := range x {
          y[i] = make([]int, len(v))
          copy(y[i], v)
        }
        x[1][0] = 4
        =>
        x: [[1 2] [4 4 5] [6 7 8 9]]
        y: [[1 2] [3 4 5] [6 7 8 9]]
    
[/code]


CC BY-SA 3.0
2015-08-29T16:44:29.577
15017
2015-08-29T17:06:23.070
2015-08-29T17:06:23.070
15003
answer