配列(リスト)をコピーするには (clone)

配列(リスト)をコピーする方法。

イミュータブルなオブジェクトのコピーは意味がないので、ここではミュータブルなリストのコピーについて。

Go言語 (golang)

copy関数を使う。別の配列へのコピーだけでなく、同じ配列の中で別の領域にコピーすることもできる。

arr := []int{0, 1, 2, 3, 4}
copy(arr[0:3], arr[2:5])
fmt.Printf("%#v\n", arr)
// => []int{2, 3, 4, 3, 4}

copy関数は配列ではなくスライスを受け入れるため、配列をコピーするには arr[:] などとしてスライスにしたものをcopy関数に渡す。

Java

Signature:

void java.lang.System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length)

別の配列へのコピーだけでなく、同じ配列の中で別の領域にコピーすることもできる。

int[] arr = new int[]{0, 1, 2, 3, 4};
System.arraycopy(arr, 2, arr, 0, 3);
for (int i = 0; i < arr.length; i++) {
    System.out.printf("%d: %d\n", i, arr[i]);
}
// 出力例
// 0: 2
// 1: 3
// 2: 4
// 3: 3
// 4: 4

java.lang.System.arraycopy | Java Platform SE 8 Javadoc
http://docs.oracle.com/javase/8/docs/api/java/lang/System.html#arraycopy-java.lang.Object-int-java.lang.Object-int-int-

PHP

配列を別の変数に代入するだけでコピーになる。

$src = array(1, 2, 3);
$dst = $src;
$src[0] = 10;

var_export($src);
// 出力結果
// array (
//   0 => 10,
//   1 => 2,
//   2 => 3,
// )

var_export($dst);
// 出力結果
// array (
//   0 => 1,
//   1 => 2,
//   2 => 3,
// )

しかも deep copy になる。

$src = array(1, array(2, 3));
$dst = $src;
$src[1][0] = 20;

var_export($src);
// 出力結果
// array (
//   0 => 1,
//   1 =>
//   array (
//     0 => 20,
//     1 => 3,
//   ),
// )

var_export($dst);
// 出力結果
// array (
//   0 => 1,
//   1 =>
//   array (
//     0 => 2,
//     1 => 3,
//   ),
// )
)

PHPでは普通の配列と連想配列は同じものなので、連想配列のコピーも同じ。

Python

NumPyでない普通の配列であれば以下の書き方を使う。

dst = src[:]

[:]スライス表記であり、この書き方で shallow copy が可能。

arr1 = [1, 2, 3]
arr2 = arr1[:]

print(id(arr1))
# => 139997756485216

print(id(arr2))
# => 139997756311240

arr2[1] = 20

print(arr2)
# => [1, 20, 3]

# コピー先で書き換えてもコピー元には影響しない
print(arr1)
# => [1, 2, 3]

なお、この書き方はNumPyではコピーにはならずビューを提供するのみになる。

スライス表記を左辺に置いてもコピーにはなるが、左辺の変数が予めなんでもいいのでとにかく何かしらの配列である必要がある。この方法では、コピー先の変数に新しいオブジェクトが代入されるのではなく、既存の配列がコピー元と同じになるように書き換えられる。

dst[:] = src

arr1 = [1, 2, 3]
arr2 = [100, 200, 300, 400]

arr2[:] = arr1

print(id(arr1))
# => 139892140826208

print(id(arr2))
# => 139892140652232

arr2[1] = 20

print(arr2)
# => [1, 20, 3]

# コピー先で書き換えてもコピー元には影響しない
print(arr1)
# => [1, 2, 3]

# この方法でコピーするとコピー先のIDは変わらない。
print(id(arr2))
# => 139892140652232

この左辺にスライス表記を使う方法はNumPyでもコピーになるが、コピー前の配列は同じ大きさである必要がある。

または別の方法としては、copyモジュールがある。

import copy

copy.copy(lst)     # shallow copy
copy.deepcopy(lst) # deep copy

copy — 浅いコピーおよび深いコピー操作 | Python 標準ライブラリ
http://docs.python.jp/2.7/library/copy.html

関連

Ruby / JRuby

Object#clone または Object#dup を使う。

shallow copy である。

Array#clone, Array#dup | Ruby 2.1 リファレンスマニュアル
http://docs.ruby-lang.org/ja/2.1.0/method/Array/i/clone.html

Perl

shallow copy の例

my @src = ...;
my @dst = @src

代入するだけでコピーされる。

変数が配列へのレファレンスの場合の例

my $src = ...;
my $dst = [@$src];

デリファレンスしてから配列を生成し直している。

JavaScript

shallow copy

dst = src.concat();

または

Array.prototype.clone = function(){
    return Array.apply(null, this)
}
dst = src.clone();

concat() というメソッドは配列を連結した新しい配列を生成するメソッドであるが、引数を与えないとコピーの目的でも使える。配列の要素自体はコピーしないので、shallow copyである。

deep copyするトリッキーな方法として一度JSONにしてしまう、というのがある。

dst = JSON.parse(JSON.stringify(src));

遠回りしているような気がしてかなり気持ち悪い。JSONで表現できない値(関数とか)があると、それはコピーできないし、この方法は推奨されない。

JavaScriptのDeepCopyでJSON.parse/stringifyを使ってはいけない | Qiita
http://qiita.com/seihmd/items/74fa9792d05278a2e898

このサイトは筆者(hydrocul)の個人メモの集合です。すべてのページは永遠に未完成です。
スポンサーリンク