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

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

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

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

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

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

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

という基本的な内容から

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

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

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

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には影響がなく別物として扱われていることがわかります。

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

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

【Java入門】配列の使い方総まとめ(宣言、初期化、追加、要素数)
更新日 : 2019年7月29日

まとめ

いかがでしたか?

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

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

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

LINEで送る
Pocket

SEからWebエンジニアへ転職した理由

侍エンジニア塾卒業生の小池さんは、以前は社内SEとして約5年ほど勤務していました。しかし業務内容は社内のヘルプデスク対応など、プログラムを書く仕事は全くなかったそうです。

SEながらプログラムを書けない現状に「将来仕事がなくなるんじゃないか」と不安を感じ、プログラミング学習を決意。

弊社スクールで学習し、無事ベンチャー企業のプログラマーとして転職に成功しました。そんな小池さんの学習法や転職体験談を伺いましたので、是非ご覧ください。

「プログラミングができないSEは仕事がなくなる」不安を感じたSEが未経験から転職成功するまで
更新日 : 2019年10月7日

書いた人

オータケ

オータケ

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

おすすめコンテンツ

あなたにぴったりなプログラミング学習プランを無料で診断!

プログラミング学習の効率を劇的に上げる学習メソッドを解説