Java | 配列のコピー

Java Java
スポンサーリンク

はじめに

配列の「コピー」は初心者がつまずきやすいポイントです。見た目は同じでも、実は「同じものを指しているだけ」になっていた…という混乱が起こりがち。ここでは、動く例で「安全にコピーする方法」と「浅いコピー/深いコピーの違い」を体感で覚えていきます。


代入はコピーではない

  • ポイント: = で配列を代入すると、同じ配列を共有します(別物は作られない)。
  • 結果: 片方を変更すると、もう片方も変わります。
int[] src = {10, 20, 30};
int[] dst = src; // コピーではなく、同じ配列を指す

dst[0] = 999;

System.out.println(src[0]); // 999(srcも変わってしまう)
System.out.println(dst[0]); // 999
Java
  • 覚え方:= は住所のメモ渡し」。家(配列そのもの)は1つ、住所を二人で持っているイメージ。

安全にコピーする3つの方法

1) for文で1つずつ手動コピー

  • 使いどころ: 仕組みを理解したい、細かい制御がしたい。
  • 長所: 直感的で万能。部分的にコピーするなど柔軟。
  • 短所: コードが長くなる。
int[] src = {10, 20, 30};
int[] dst = new int[src.length];

for (int i = 0; i < src.length; i++) {
    dst[i] = src[i];
}

dst[0] = 999;

System.out.println(src[0]); // 10(独立しているので変わらない)
System.out.println(dst[0]); // 999
Java

2) Arrays.copyOf を使う(いちばん簡単)

  • 使いどころ: まるごとコピーしたいときの定番。
  • 長所: 短くて読みやすい。長さを変えて拡張もできる。
  • 短所: 部分コピーは不得意。
import java.util.Arrays;

int[] src = {10, 20, 30};
int[] dst = Arrays.copyOf(src, src.length); // 完全コピー

System.out.println(Arrays.toString(dst)); // [10, 20, 30]

// 長さを増やすことも可能(足りない分は0で埋まる)
int[] grown = Arrays.copyOf(src, 5);
System.out.println(Arrays.toString(grown)); // [10, 20, 30, 0, 0]
Java

3) System.arraycopy を使う(高速で部分コピー向き)

  • 使いどころ: 一部だけコピーしたい、大量データで速度を重視したい。
  • 長所: 部分コピー・高速。
  • 短所: 引数が多くてやや分かりにくい。
int[] src = {10, 20, 30, 40, 50};
int[] dst = new int[3];

// srcのインデックス1(20)から3要素ぶんをdstにコピー
System.arraycopy(src, 1, dst, 0, 3);

System.out.println(java.util.Arrays.toString(dst)); // [20, 30, 40]
Java

参照型の落とし穴:浅いコピーと深いコピー

配列の中身が「数値や文字」などの値型なら問題は少ないですが、「オブジェクト」を入れる場合は注意が必要です。

浅いコピー(シャローコピー)

  • 意味: 配列自体は新しくなるが、中のオブジェクトの参照は同じものを指す。
  • 結果: 片方でオブジェクトの中身を変えると、もう片方も変わる。
class Person {
    String name;
    Person(String name) { this.name = name; }
}

Person[] src = { new Person("Alice"), new Person("Bob") };

// 配列は新しくなるが、中身(Person参照)は共有される
Person[] shallow = java.util.Arrays.copyOf(src, src.length);

shallow[0].name = "Changed";

System.out.println(src[0].name);     // Changed(影響が及ぶ)
System.out.println(shallow[0].name); // Changed
Java

深いコピー(ディープコピー)

  • 意味: 中のオブジェクトも新しく作り直してコピーする。
  • 結果: 片方を変えても、もう片方には影響しない。
class Person {
    String name;
    Person(String name) { this.name = name; }
}

Person[] src = { new Person("Alice"), new Person("Bob") };

// 1件ずつ新しいPersonを作ってコピーする(ディープコピー)
Person[] deep = new Person[src.length];
for (int i = 0; i < src.length; i++) {
    deep[i] = new Person(src[i].name); // 中身を複製
}

deep[0].name = "Changed";

System.out.println(src[0].name);  // Alice(影響なし)
System.out.println(deep[0].name); // Changed
Java
  • 覚え方: 「浅いコピー=箱だけ別、新しい中身は作らない」「深いコピー=箱も中身も別物」。

どれを使えばいい?簡単な目安

  • とりあえず完全コピーしたい: Arrays.copyOf
  • 一部だけ、または速度重視: System.arraycopy
  • 学習や細かい制御: for文で手動コピー
  • オブジェクト配列で安全に独立させたい: 深いコピー(中身を新しく作る)

練習課題

  1. 完全コピーの練習:
    • タスク: int[] a = {1,2,3,4}Arrays.copyOf でコピーし、片方だけ書き換えて違いを出力。
    • ゴール: 「独立している」ことを確認する。
  2. 部分コピーの練習:
    • タスク: int[] b = {5,6,7,8,9} から System.arraycopy で中央の3つだけ取り出す。
    • ゴール: 引数の意味(元の開始位置、コピー数、先の開始位置)を説明できる。
  3. 浅い/深いコピーの練習:
    • タスク: Book クラス(titleだけ持つ)を使った配列で、浅いコピーと深いコピーをそれぞれ試す。
    • ゴール: 「浅いコピーでは中身が共有される」感覚をつかむ。

解答例と解説

課題1:完全コピーの練習

問題:
int[] a = {1,2,3,4}Arrays.copyOf でコピーし、片方だけ書き換えて違いを出力。

解答例:

import java.util.Arrays;

public class CopyTest1 {
    public static void main(String[] args) {
        int[] a = {1, 2, 3, 4};
        int[] b = Arrays.copyOf(a, a.length); // 完全コピー

        b[0] = 99; // コピー先だけ変更

        System.out.println(Arrays.toString(a)); // [1, 2, 3, 4]
        System.out.println(Arrays.toString(b)); // [99, 2, 3, 4]
    }
}
Java

解説:

  • Arrays.copyOf は新しい配列を作って中身をコピーする。
  • b を変更しても a には影響しない。
  • 「独立した配列」ができていることを確認できる。

課題2:部分コピーの練習

問題:
int[] b = {5,6,7,8,9} から System.arraycopy で中央の3つだけ取り出す。

解答例:

import java.util.Arrays;

public class CopyTest2 {
    public static void main(String[] args) {
        int[] b = {5, 6, 7, 8, 9};
        int[] c = new int[3]; // コピー先の配列を用意

        // bのインデックス1から3要素をcにコピー
        System.arraycopy(b, 1, c, 0, 3);

        System.out.println(Arrays.toString(c)); // [6, 7, 8]
    }
}
Java

解説:

  • System.arraycopy(元配列, 元の開始位置, コピー先配列, コピー先の開始位置, 要素数)
  • 今回は b[1] から3つ → [6,7,8] がコピーされる。
  • 部分コピーや高速コピーに便利。

課題3:浅いコピーと深いコピーの練習

問題:
Book クラス(titleだけ持つ)を使った配列で、浅いコピーと深いコピーをそれぞれ試す。

浅いコピー(シャローコピー)

class Book {
    String title;
    Book(String title) { this.title = title; }
}

public class ShallowCopy {
    public static void main(String[] args) {
        Book[] src = { new Book("Java入門"), new Book("C入門") };

        // 浅いコピー(参照だけコピー)
        Book[] shallow = java.util.Arrays.copyOf(src, src.length);

        shallow[0].title = "Python入門"; // コピー先の中身を変更

        System.out.println(src[0].title);     // Python入門(元も変わる)
        System.out.println(shallow[0].title); // Python入門
    }
}
Java

解説:

  • 配列自体は新しくなるが、中の Book オブジェクトは同じものを指す。
  • 片方を変更するともう片方も変わる。

深いコピー(ディープコピー)

class Book {
    String title;
    Book(String title) { this.title = title; }
}

public class DeepCopy {
    public static void main(String[] args) {
        Book[] src = { new Book("Java入門"), new Book("C入門") };

        // 深いコピー(中身も新しく作る)
        Book[] deep = new Book[src.length];
        for (int i = 0; i < src.length; i++) {
            deep[i] = new Book(src[i].title);
        }

        deep[0].title = "Python入門"; // コピー先だけ変更

        System.out.println(src[0].title);  // Java入門(元は変わらない)
        System.out.println(deep[0].title); // Python入門
    }
}
Java

解説:

  • 新しい Book オブジェクトを作ってコピーするので、完全に独立。
  • 片方を変更してももう片方には影響しない。

まとめ

  • 課題1: Arrays.copyOf → 独立した配列が作れる。
  • 課題2: System.arraycopy → 部分コピーや高速コピーに便利。
  • 課題3: 浅いコピーは参照を共有、深いコピーは中身も新しく作る。

👉 ここまでで「配列コピーの基本」と「浅い/深いコピーの違い」が理解できるはずです。
次のステップとしては、実際にコードを動かして結果を確認することが大事です。

Java
スポンサーリンク
シェアする
@lifehackerをフォローする
スポンサーリンク
タイトルとURLをコピーしました