HOWTO · Java

Java で ArrayList をディープコピーする

この記事では、Java で ArrayList をディープコピーする方法を紹介します。

Java 言語では、ArrayList をコピーする場合、2つの方法があります。ディープコピーまたはシャローコピーを実行します。

ディープコピーでは、完全に新しいオブジェクトが作成されるため、古いオブジェクトの状態の変化は、新しく作成されたインスタンスの変更を反映しません。

浅いコピーでは、既存のオブジェクトへの参照を持つ新しいオブジェクトを作成します。したがって、古いオブジェクトの状態の変更は、新しく作成されたインスタンスに反映されます。

上記の 2つの定義をよりよく説明する図:

深いコピーと浅いコピー

ディープコピーとは

既存のオブジェクトから新しいオブジェクトを作成することをディープコピーと呼びます。このタイプのコピーには、現在のオブジェクトへの参照はありません。ディープコピーは反復的なタスクです。内部メンバー変数とオブジェクトを新しいインスタンスに再帰的にコピーします。

このタイプのクローンは、ネストされたすべてのオブジェクトをコピーし、ソースとコピーの間に参照リンクを残しません。このクローン作成では、すべてのプリミティブ(byte、short、int)および非プリミティブ(String、Array、Class)データ型が正確にコピーされます。

オブジェクトをディープコピーする場合は、Object クラスの Cloneable インターフェイスの clone() メソッドをオーバーライドします。

Java での ArrayList のディープコピーのコードサンプルを次に示します。

package deepVsShallowCopy;

public class Car implements Cloneable {
  String name;

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public Car(String name) {
    this.name = name;
  }

  @Override
  public Car clone() throws CloneNotSupportedException {
    Car newCar = (Car) super.clone();
    newCar.setName(newCar.getName());
    return newCar;
  }
}

上記の POJO クラスでは、getters および setters とともに、clone メソッドをオーバーライドする必要があります。clone() メソッドは、オーバーライドされたメソッドでスローする必要がある CloneNotSupportedException をスローします。

既存のオブジェクトのクローンを作成するには、バージョンを実装する必要があります。Car クラスのすべてのメンバー変数を新しいインスタンスにコピーする必要があります。

以下は、ArrayList をディープコピーするためのドライバークラスの実装です。

package deepVsShallowCopy;

import java.util.ArrayList;
import java.util.List;

public class DeepCopyArrayList {
  public static void main(String[] args) throws CloneNotSupportedException {
    // instantiate car instance
    Car oldCar = new Car("Ambassador");
    // instantiate an arraylist
    List<Car> oldList = new ArrayList<Car>();
    // add oldCar object into the list
    oldList.add(oldCar);

    // instantiate newList
    List<Car> newList = new ArrayList<Car>();
    // add object created using clone() into list
    newList.add(oldList.get(0).clone());
    // rename field in newList car element
    newList.get(0).setName("Rolls Royce");

    System.out.println("oldName : " + oldList.get(0).getName()); // oldName : Ambassador
    System.out.println("newName : " + newList.get(0).getName()); // newName : Rolls Royce
  }
}

上記の実装では、Car クラスのオーバーライドされた clone() メソッドが呼び出され、完全に新しいインスタンスが返されます。そして、この新しく作成されたインスタンスが ArrayList に追加されます。

また、新しく作成されたオブジェクトでいくつかの操作を行っても、これは古いオブジェクト参照には影響しません。

また、ArrayList を個別に反復処理する場合は、Car のオブジェクトに対して clone() メソッドを呼び出す必要があります。

for (Car c : oldList) {
  newList.add(c.clone());
}