【Java】ディープコピーの実装方法

Java

【Java】ディープコピーの実装方法

Javaで「ディープコピー」したいのですが、どう実装すればよいですか?
Javaの場合、「ディープコピー」の方法は様々なんだ。プロジェクトによって最適な方法で実装することをお勧めするよ。
具体的にはどのような方法があるのですか?
本記事では、Javaで「ディープコピー」する方法を紹介していきます。

スポンサーリンク

ディープコピーの実装方法

[方法1] Cloneableを使ってディープコピー(clone)する

Javaで提供されているCloneableインタフェースを実装することで、ディープコピーすることができます。

1. Cloneableをimplementsし、cloneメソッドを実装する

Cloneableを使ってディープコピーを実装する場合は、コピー対象となるオブジェクトがCloneableをimplementsする必要があります

そしてcloneメソッドをOverrideしclone処理を書きます。

具体的なサンプルは以下の通りです。

cloneメソッド内では、Schoolクラスのcloneの作成、そしてリストで保持しているStudentクラスのcloneを作成しています。

package entity;

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

/**
 * 学校クラス
 */
public class School implements Cloneable {
  /**
   * 学校名
   */
  private String schoolName;
  /**
   * 生徒リスト
   */
  private List<Student> studentList;
  /**
   * ディープコピー
   */
    @Override
    public School clone() {
    	School school = null;

        try {
        	school = (School) super.clone();

        	// 生徒リストをコピー
        	school.studentList = new ArrayList<Student>();
        	if (this.studentList != null) {
        		for (Student sudent : this.studentList) {
        			school.studentList.add(sudent.clone());
        		}
        	}

        }catch (Exception e){
        	school = null;
        }
        return school;
    }

  public String getSchoolName() {
    return schoolName;
  }

  public void setSchoolName(String schoolName) {
    this.schoolName = schoolName;
  }

  public List<Student> getStudentList() {
    return studentList;
  }

  public void setStudentList(List<Student> studentList) {
    this.studentList = studentList;
  }
}
package entity;

/**
 * 生徒クラス
 */
public class Student  implements Cloneable {

  /**
   * 名前
   */
  private String name;
  /**
   * ディープコピー
   */
    @Override
    public Student clone() {
    	Student student = null;

        try {
        	student = (Student) super.clone();

        }catch (Exception e){
        	student = null;
        }
        return student;
    }
  public String getName() {
    return name;
  }

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

2. cloneでディープコピーする

Cloneableをimplementsしているオブジェクトさえ作れれば、後はclone()を呼び出すことでディープコピーすることができます。

School school = new School();

// ディープーコピー
School copySchool = school.clone();

スポンサーリンク

[方法2] SerializationUtilsライブラリで、ディープコピー(clone)する

apache.commons.lang.SerializationUtilsクラスが提供しているcloneメソッドでディープコピーすることができます。

SerializationUtilsクラスは、Apache Commons Lang(commons-lang×.jar ※×はバージョン)のライブラリを使うことで利用可能です。

1. Serializableをimplementsする

SerializationUtilsクラスで「ディープコピー」するためには、コピー対象のオブジェクトがSerializableをimplementsしていなければコピーできません

そのため、コピー対象のオブジェクトはSerializableをimplementsします。

具体的なサンプルは以下の通りです。

package entity;

import java.io.Serializable;
import java.util.List;

/**
 * 学校クラス
 */
public class School2 implements Serializable {

  /**
   * 学校名
   */
  private String schoolName;
  /**
   * 生徒リスト
   */
  private List<Student2> studentList;

  public String getSchoolName() {
    return schoolName;
  }
  public void setSchoolName(String schoolName) {
    this.schoolName = schoolName;
  }
  public List<Student2> getStudentList() {
    return studentList;
  }
  public void setStudentList(List<Student2> studentList) {
    this.studentList = studentList;
  }

}
package entity;

import java.io.Serializable;

/**
 * 生徒クラス
 */
public class Student2 implements Serializable {

  /**
   * 名前
   */
  private String name;

  public String getName() {
    return name;
  }

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

2. cloneでディープコピーする

コピー対象のオブジェクトがSerializableをimplementsしていれば、後はclone()を呼び出すことでディープコピーすることができます。

School2 school = new School2();

// ディープーコピー
School2 copySchool = SerializationUtils.clone(school);

スポンサーリンク

[方法3] コンストラクタ等で詰め替える

コンストラクタ等を利用して各項目を詰め替えることで「ディープコピー」することができます。項目が多いと大変 または メンテナンス性が悪いなどのデメリットはありますが、シンプルな方法です。

1. コンストラクタで詰め替える処理を実装する

コンストラクタで詰め替える処理を実装します。

具体的なサンプルは以下の通りです。

package entity;

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

/**
 * 学校クラス
 */
public class School3 {

  /**
   * コンストラクタ
   */
  public School3() {}

  /**
   * コピー用コンストラクタ
   * @param schoolName 学校名
   * @param studentList 生徒リスト
   */
  public School3(School3 school) {
    this.schoolName = school.getSchoolName();

    if (school.getStudentList() != null) {
      this.studentList = new ArrayList<Student3>();

      for(Student3 student : school.getStudentList()) {
        this.studentList.add(new Student3(student));
      }
    }
  }

  /**
   * 学校名
   */
  private String schoolName;
  /**
   * 生徒リスト
   */
  private List<Student3> studentList;

  public String getSchoolName() {
    return schoolName;
  }
  public void setSchoolName(String schoolName) {
    this.schoolName = schoolName;
  }
  public List<Student3> getStudentList() {
    return studentList;
  }
  public void setStudentList(List<Student3> studentList) {
    this.studentList = studentList;
  }
}
package entity;

/**
 * 生徒クラス
 */
public class Student3 {

  /**
   * コンストラクタ
   */
  public Student3() {}

  /**
   * コピー用コンストラクタ
   * @param name 名前
   */
  public Student3(Student3 student) {
    this.name = student.getName();
  }

  /**
   * 名前
   */
  private String name;

  public String getName() {
    return name;
  }

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

2. インスタンス生成時にディープコピーする

コピー用のコンストラクタを用意したら、後はインスタンス生成時に引数としてコピー元オブジェクトを渡すだけで「ディープコピー」することができます。

School3 school = new School3();

// ディープーコピー
School3 copySchool = new School3(school);

スポンサーリンク

[方法4] ディープコピーの処理を自作する

最後は「ディープコピー」の処理を自作する方法です。

1. ディープコピー用の関数を作る

ディープコピー用の関数を作成します。

具体的なサンプルは以下の通りです。

※以下の関数では、コピー対象のオブジェクトはSerializableをimplementsしている必要があります。

/**
 * ディープコピー
 * @param obj コピー元オブジェクト
 * @return コピー先オブジェクト
 * @throws IOException
 * @throws ClassNotFoundException
 */
public static Object deepcopy(Object obj) throws IOException, ClassNotFoundException {
  Object copy = null;

  try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
      ObjectOutputStream oos = new ObjectOutputStream(baos);) {

    // 符号化されたオブジェクトのデータを保持する配列を取得
    oos.writeObject(obj);
    byte[] buff = baos.toByteArray();

    // バイト配列から、オブジェクトを複合化
    try (ByteArrayInputStream bais = new ByteArrayInputStream(buff);
        ObjectInputStream os = new ObjectInputStream(bais);) {
      copy = os.readObject();
    }
  }

  return copy;
}

2. 関数を呼び出しディープコピーする

自作で作成した関数を呼び出し「ディープコピー」を行います。

School2 school = new School2();

// ディープーコピー
School2 copySchool = (School2) deepcopy(school);

\ 参考になりましたか? /

© 2020 ITを分かりやすく解説