Java | 基礎文法:配列のコピー

Java Java
スポンサーリンク

配列コピーの全体像

「配列のコピー」は、既存の配列から新しい配列を作って中身を複製することです。Java では System.arraycopyArrays.copyOfArrays.copyOfRangeclone() が代表的な方法です。重要なのは「浅いコピー(要素参照をそのまま複製)」と「深いコピー(要素内容も再構築)」の違いを理解し、用途に合わせて使い分けることです。プリミティブ配列は値が複製され、オブジェクト配列は参照が複製されるため、変更が伝播するかどうかが根本的に違います。


標準的なコピー手段

Arrays.copyOf(サイズ変更も可能)

Arrays.copyOf(src, newLength) は最も簡単で安全な「丸ごとコピー」。サイズを変えられるため、拡張や縮小にも使えます。

int[] src = {1, 2, 3};
int[] dst = java.util.Arrays.copyOf(src, 5);  // [1,2,3,0,0](拡張、末尾は0で埋まる)
Java

Arrays.copyOfRange(部分コピー)

区間 [from, to)(from 包括・to 排他)でコピーします。範囲外なら例外、足りない分は型のデフォルト値で埋まります。

int[] src = {10, 20, 30, 40, 50};
int[] sub = java.util.Arrays.copyOfRange(src, 1, 4); // [20,30,40]
Java

System.arraycopy(最速の低レベル)

コピー先を自前で用意して、区間を指定して転送します。大量コピーや既存配列の一部上書きに向いています。

int[] src = {10, 20, 30, 40, 50};
int[] dst = new int[3];
System.arraycopy(src, 1, dst, 0, 3); // src[1..3] → dst[0..2] → [20,30,40]
Java

clone(丸ごとの浅いコピー)

array.clone() は同型・同サイズで丸ごとコピーします。中身がプリミティブなら値の複製、オブジェクト配列なら参照の複製(浅いコピー)です。

String[] a = {"A", "B"};
String[] b = a.clone(); // 参照は同じ要素を指す(浅い)
Java

浅いコピーと深いコピー(ここが最重要)

プリミティブ配列は「値コピー」

  • 結果: 片方を変更しても、もう片方には影響しません。
int[] a = {1, 2};
int[] b = a.clone();
b[0] = 99;
System.out.println(java.util.Arrays.toString(a)); // [1,2](影響なし)
Java

オブジェクト配列は「参照コピー」(浅い)

  • 結果: 要素オブジェクトを更新すると、両方から同じオブジェクトが見えるため変更が伝播します。
class Box { int v; Box(int v){this.v=v;} }
Box[] a = { new Box(1), new Box(2) };
Box[] b = a.clone();      // 参照を複製
b[0].v = 99;              // 同じ Box を指している
System.out.println(a[0].v); // 99(伝播)
Java

深いコピーが必要なとき

  • 方針: 各要素を新規インスタンスに置き換える(コピーコンストラクタ、copy() メソッド、レコードなら成分で再構築)。
Box[] deep = java.util.Arrays.stream(a)
    .map(x -> new Box(x.v))   // 新しい Box を作る
    .toArray(Box[]::new);
deep[0].v = 7;
System.out.println(a[0].v); // 99(元は不変)
Java

2次元配列(配列の配列)は「外側だけ clone しても内側は同じ参照」のままなので、内側も個別にコピーしないと深いコピーにはなりません。


性能とメモリの観点

System.arraycopy は高速

  • 用途: 大量の連続領域コピーに最適。Arrays.copyOf/copyOfRange も内部で使うため十分速いですが、既存配列へ部分上書きする場面では arraycopy が直接的です。

小さなコピーは可読性優先

  • 指針: 使い分けで迷ったら Arrays.copyOf を基本に。コードが短く、境界の取り違えが減ります。

不要なコピーは避ける

  • 例: イミュータブルな配列を使う場面で毎回コピーするとメモリ・CPUを消耗します。変更しない契約なら共有、変更する可能性があるなら防御的コピー(受け取り時に一度だけコピー)を選びます。

よくある落とし穴と対策

浅いコピーで意図せず共有

  • 対策: 要素が可変なら、必要に応じて深いコピーに切り替える。コピー後に変更する側が「独立性」を必要とする場合は要素再構築が必須。

範囲指定の勘違い(排他・包括)

  • 対策: copyOfRange(src, from, to)to は「排他」。インデックスは常に「0 ≤ index < length」。System.arraycopylength は「要素数」であり、終端インデックスではありません。

2次元配列を clone して安心しない

  • 対策: 外側 clone の後、各行(内側配列)を個別に clone/copyOf する。
int[][] g = {{1,2},{3,4}};
int[][] shallow = g.clone();                 // 行参照は共有
int[][] deep = new int[g.length][];
for (int i = 0; i < g.length; i++) deep[i] = g[i].clone(); // 行もコピー
Java

List に変換したときの「ビュー」扱いの違い

  • 対策: Arrays.asList(array) は固定サイズのリストビュー。要素変更は反映されるが、追加・削除は不可。独立した可変リストが欲しいなら new ArrayList<>(Arrays.asList(array))

例題で身につける

例 1: 配列の一部を別配列へコピー(最小コスト)

int[] src = {10, 20, 30, 40, 50};
int[] dst = new int[3];
System.arraycopy(src, 1, dst, 0, 3); // [20,30,40]
System.out.println(java.util.Arrays.toString(dst));
Java

例 2: サイズ拡張しながらコピー(余白を確保)

int[] src = {1, 2, 3};
int[] bigger = java.util.Arrays.copyOf(src, src.length + 2); // [1,2,3,0,0]
Java

例 3: オブジェクト配列の深いコピー(要素再構築)

class Box { int v; Box(int v){this.v=v;} }
Box[] a = { new Box(1), new Box(2) };
Box[] deep = java.util.Arrays.stream(a)
    .map(b -> new Box(b.v))
    .toArray(Box[]::new);
deep[0].v = 7;
System.out.println(a[0].v); // 1(独立)
Java

例 4: 2次元配列の安全な深いコピー

int[][] g = {{1,2,3},{4,5,6}};
int[][] copy = new int[g.length][];
for (int r = 0; r < g.length; r++) {
    copy[r] = java.util.Arrays.copyOf(g[r], g[r].length);
}
copy[0][0] = 99;
System.out.println(g[0][0]); // 1(元は不変)
Java

例 5: 部分コピーでサブ配列を取り出す

int[] src = {0,1,2,3,4,5,6};
int[] evens = java.util.Arrays.copyOfRange(src, 0, src.length); // 全体
int[] middle = java.util.Arrays.copyOfRange(src, 2, 5); // [2,3,4]
Java

仕上げのアドバイス(重要部分のまとめ)

配列コピーは「何を独立させたいか」を決めるのが最優先です。プリミティブは浅いコピーで十分、オブジェクトは浅いコピーだと参照共有になるため、変更が伝播します。独立性が必要なら要素の再構築で深いコピーにする。全体・部分・サイズ変更は Arrays.copyOf/Range、既存配列への転送は System.arraycopy、丸ごとは clone。範囲の排他・包括を誤らず、2次元は内側までコピー——この型が身につけば、安全かつ効率よく配列を扱えます。

タイトルとURLをコピーしました