【Java入門】配列のコピー(clone、arraycopy、ShallowとDeep)

こんにちは!フリーランスのオータケです。

Java言語では配列をコピーする方法が幾つかあります。

コードの長さを気にせず書く方法としては配列を2つ用意してfor文を使いコピー元の配列からコピー先の配列に代入していく方法がありますが、

もっと簡潔に書く方法をわかりやすくご紹介したいと思います!

この記事では、配列をコピーする方法について

・clone、arraycopyを使った配列のコピー

という基本的な内容から

・ArrayListのシャローコピーとディープコピー

など具体的な内容についても解説していきます。

今回は配列をコピーする方法について、使い方をわかりやすく解説します!

なお、Javaの記事については、こちらにまとめています。

目次

clone、arraycopyを使った配列のコピー

配列をコピーする方法をご紹介します!

幾つか方法があるのでご自身の状況にあった使い方を選んで下さい。

cloneでのコピー(DeepCopy)

とてもシンプルな方法をご紹介します。

cloneメソッドを使って、次のように書けば配列のコピーができます。

データ型名[] コピー元の配列名 = new データ型[要素数];
データ型名[] コピー先の配列名 = コピー元の配列名.close();

では実際にコピー元の配列の値がコピー先の配列にコピーされているのか見てみたいと思います。

次の例をご覧ください。

import java.util.Arrays;

public class Main {
 
    public static void main(String[] args) {
        int[] foo = new int[10]; // 元の配列
         
        for(int i = 0; i < foo.length; i++) {
            foo[i] = i + 1;
        }
         
        int[] bar = foo.clone();
        
        System.out.println(Arrays.toString(foo));
        System.out.println(Arrays.toString(bar));
    }

}

実行結果:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

このサンプルコードでは、配列fooに1~10の数値を代入し、配列barにコピーした後にbarの中身を表示しています。

しっかりコピーされていますね!

arraycopyでのコピー

もう一つのコピー方法を見てみましょう。

arraycopyを使って以下のように記述します。

System.arraycopy(コピー元配列, コピー元配列のコピー開始位置, コピー先配列, コピー先配列の開始位置, コピーの個数)

なおarraycopyを使う場合、コピー先の値がコピー元の値で上書きされますので注意が必要です。

サンプルコードで確認しましょう。

import java.util.Arrays;

public class Main {
 
    public static void main(String[] args) {
        int[] foo = new int[10]; // 元の配列
        int[] bar = new int[10]; // コピー先の配列
         
        for(int i = 0; i < foo.length; i++) {
            foo[i] = i + 1;
        }
        
        System.arraycopy(foo, 0, bar, 0, 10);
        
        System.out.println(Arrays.toString(foo));
        System.out.println(Arrays.toString(bar));
    }

}

実行結果:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

ArrayListのシャローコピーとディープコピー

今度はArrayListを用いたコピー方法を見てみましょう。

ArrayListのコピーにはシャローコピー(ShallowCopy)とディープコピー(DeepCopy)があります。

それぞれの言葉の意味としては

  • Shallow(浅い)
  • Deep(深い)

になり、配列の各要素にある中身を厳密にコピーするかどうかを意味します。

シャローコピーの場合は参照のみコピーを行います。

ディープコピーの場合は別の参照先に構造と値をコピーします。

それぞれのコピー方法は用途によって使い分ける必要があり、このあたりをよく理解しないで使ってしまうと思わぬ不具合を生み出す可能性もあります。

この2つの違いをサンプルコードで確認していきましょう。

シャローコピー(ShallowCopy)

シャローコピーは以下のように定義時に代入する形で記述します。

ArrayList<Object名> コピー元の配列名 = new ArrayList<>();
ArrayList<Object名> コピー先の配列名 = コピー元の配列名;

サンプルコードで確認しましょう。

import java.util.ArrayList;

public class Main {
 
    public static void main(String[] args) {
        // コピー元のArrayList
        ArrayList<String> src_list = new ArrayList<>();
 
        for(int i = 0; i < 10; i++) {
            src_list.add("AAA");
        }
        
        // コピー先のArrayListオブジェクトに代入してコピー(ShallowCopy)
        ArrayList<String> dst_list = src_list;
        
        // コピー先を変更
        for(int i = 0; i < dst_list.size(); i++) {
            dst_list.set(i, "BBB");
        }
         
        // コピー元とコピー先を出力表示
        System.out.println(src_list);
        System.out.println(dst_list);
    }

}

実行結果:

[BBB, BBB, BBB, BBB, BBB, BBB, BBB, BBB, BBB, BBB]
[BBB, BBB, BBB, BBB, BBB, BBB, BBB, BBB, BBB, BBB]

このサンプルコードでは、src_listとdst_listの両方の値がBBBと書き換わってしまっています。

意図せず書き換えてしまって不具合を生む可能性もありますので注意しましょう。

ディープコピー(DeepCopy)

ディープコピーの場合はnew句を使いインスタンスを生成し、その引数にコピー元の配列を渡して記述します。

ArrayList<Object名> コピー元の配列名 = new ArrayList<>();
ArrayList<Object名> コピー先の配列名 = new ArrayList<>(コピー元の配列名);

サンプルコードで確認しましょう。

import java.util.ArrayList;

public class Main {
 
    public static void main(String[] args) {
        // コピー元のArrayList
        ArrayList<String> src_list = new ArrayList<>();
 
        for(int i = 0; i < 10; i++) {
            src_list.add("AAA");
        }
        
        // ArrayListのインスタンス生成時にコピー元を引数で渡してコピー(DeepCopy)
        ArrayList<String> dst_list = new ArrayList<>(src_list);
        
        // コピー先を変更
        for(int i = 0; i < dst_list.size(); i++) {
            dst_list.set(i, "BBB");
        }
         
        // コピー元とコピー先を出力表示
        System.out.println(src_list);
        System.out.println(dst_list);
    }

}

実行結果:

[AAA, AAA, AAA, AAA, AAA, AAA, AAA, AAA, AAA, AAA]
[BBB, BBB, BBB, BBB, BBB, BBB, BBB, BBB, BBB, BBB]

このサンプルコードでは、ArrayListのインスタンス生成時にコピー元を引数で渡してディープコピーを行っています。

ディープコピーでは配列の構造だけでなく中に入っている文字列自体もコピーされるため、dst_listの文字列を変更してもsrc_listには影響がなく別物として扱われていることがわかります。

配列の宣言、初期化、値の追加など知りたい!

配列の宣言、初期化、値の追加、要素数の取得、値のソートや結合についてなど、詳しく知りたい方はこちらもご覧ください!

まとめ

いかがでしたか?

配列コピーの方法からArrayListを用いたコピーの方法を見てきました。

それぞれ使い方がやや違ったり、シャローコピーやディープコピーの考え方で少し混乱してしまうかもしれません。

この記事で記載したサンプルプログラムを元に、いろいろ試してみるのもいいかと思います!

この記事を書いた人

30歳、フリーランスプログラマ。中学の頃よりプログラミングに興味を持ちゲーム開発やWebサイト構築などを経験
新しいフレームワークやライブラリに興味があり革新的な機能が含まれていると泣いて喜ぶ。

目次