Java オブジェクトのclone - シャローコピーとディープコピー
2009/06/04 11:21Update
Java オブジェクトのディープコピーの実装方法をサンプルから学びます。
Javaクローン(clone)の概要と利用方法について、次の記事をご参照ください。
Java オブジェクトのclone 概要
まず、シャローコピーとディープ(deep)コピーとは何かから説明したいと思います。
シャローコピー はshallow copyのカタカナで、浅い複製という意味です。
ディープコピー はdeep copyのカタカナで、深い複製という意味です。
では、いったい何か浅い・深いとしているのでしょうか。
サンプルからみてみましょう。
Java オブジェクトのclone 概要
記事のサンプルを少し修正してみます。
TestClone.java
実行結果:
ご覧の通り、
clonedList.equals(myClone1.getListValue())とelement2.equals(element1)はtrueを返しています。
clonedListを修正した場合、myClone1.listValueも修正されたことも確認できています。
即ち、コピー元とコピー先オブジェクトのフィールド値は同じオブジェクトを参照していると言えます。
Java Object#clone()は単なるシャローコピーであり、コピー先の参照型のフィールドはコピー元の参照先と同じになります。
そのため、コピー先の参照型のフィールド値を変更するとコピー元まで変更されてしまいます。
その反面、コピー先の参照型のフィールド値を変更してもコピー元は変更されないのはディープコピーです。
では、ディープコピー機能をどうやって実装できるの?
答えは、オーバーライドしたclone()メソッドに、参照型のフィールドにもclone()処理をかけます。
もちろん、フィールドの型はCloneableインタフェースを実装しなければなりません。
例:
.
Java オブジェクトのclone 概要
まず、シャローコピーとディープ(deep)コピーとは何かから説明したいと思います。
シャローコピー はshallow copyのカタカナで、浅い複製という意味です。
ディープコピー はdeep copyのカタカナで、深い複製という意味です。
では、いったい何か浅い・深いとしているのでしょうか。
サンプルからみてみましょう。
サンプルからわかるJavaオブジェクトのシャローコピー
Java オブジェクトのclone 概要
記事のサンプルを少し修正してみます。
TestClone.javapublic class TestClone {
public static void main(String[] args) {
//コピー元オブジェクト
MyClone myClone1 = new MyClone("clone1");
//String, boolean, String型のフィールドに値を設定
myClone1.setBoolValue(true);
myClone1.setIntValue(100);
//Listフィールド
List <Element>listValue = new ArrayList<Element>();
listValue.add(new Element("ListElement1"));
listValue.add(new Element("ListElement2"));
listValue.add(new Element("ListElement3"));
myClone1.setListValue(listValue);
//String, boolean, String以外の型のフィールド
Element element1 = new Element("element1");
myClone1.setElement(element1);
//クローン(コピー先オブジェクト)
MyClone myClone2 = (MyClone)myClone1.clone();
if (myClone2 != null) {
//String, int, boolean型
System.out.println("myClone2.name=" + myClone2.getName()
+ " myClone2.boolValue=" + myClone2.isBoolValue()
+ " myClone2.intValue=" + myClone2.getIntValue() );
//List<Element>とElement型
List clonedList = myClone2.getListValue();
Element element2 = myClone2.getElement();
System.out.println("myClone2.listValue.equals(myClone1.listValue):" + clonedList.equals(myClone1.getListValue()));
System.out.println("myClone2.listValue.size():" + clonedList.size());
System.out.println("myClone2.element.equals(myClone1.element):" + element2.equals(element1));
System.out.println("myClone2.element.name:" + element2.getName());
//コピー先オブジェクトの「List<Element>とElement型」のフィールド値を修正してみる
clonedList.add("ListElement4");
element2.setName("Element2");
System.out.println("=====myClone2.listValueとmyClone2.elementが修正したよ=====");
System.out.println("myClone1.listValue.size():" + listValue.size());
System.out.println("myClone1.element.name:" + element1.getName());
} else {
System.out.println("Clone Not Supported");
}
}
}
class MyClone implements Cloneable {
private int intValue;
private boolean boolValue;
private String name;
private List <Element>listValue;
private Element element;
public MyClone(String name) {
this.name = name;
}
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
return null;
}
}
...//setterとgetterメソッド(略)
}
class Element implements Cloneable {
private String name;
public Element (String name) {
this.name = name;
}
...//setterとgetterメソッド(略)
}
実行結果:
C:\clone>javac *.java
C:\clone>java TestClone
myClone2.name=clone1 myClone2.boolValue=true myClone2.intValue=100
myClone2.listValue.equals(myClone1.listValue):true
myClone2.listValue.size():3
myClone2.element.equals(myClone1.element):true
myClone2.element.name:element1
=====myClone2.listValueとmyClone2.elementが修正したよ=====
myClone1.listValue.size():4
myClone1.element.name:Element2
C:\clone>java TestClone
myClone2.name=clone1 myClone2.boolValue=true myClone2.intValue=100
myClone2.listValue.equals(myClone1.listValue):true
myClone2.listValue.size():3
myClone2.element.equals(myClone1.element):true
myClone2.element.name:element1
=====myClone2.listValueとmyClone2.elementが修正したよ=====
myClone1.listValue.size():4
myClone1.element.name:Element2
ご覧の通り、
clonedList.equals(myClone1.getListValue())とelement2.equals(element1)はtrueを返しています。
clonedListを修正した場合、myClone1.listValueも修正されたことも確認できています。
即ち、コピー元とコピー先オブジェクトのフィールド値は同じオブジェクトを参照していると言えます。
結論:シャローコピーとディープコピー
Java Object#clone()は単なるシャローコピーであり、コピー先の参照型のフィールドはコピー元の参照先と同じになります。
そのため、コピー先の参照型のフィールド値を変更するとコピー元まで変更されてしまいます。
その反面、コピー先の参照型のフィールド値を変更してもコピー元は変更されないのはディープコピーです。
ディープコピーの実装方法
では、ディープコピー機能をどうやって実装できるの?
答えは、オーバーライドしたclone()メソッドに、参照型のフィールドにもclone()処理をかけます。
もちろん、フィールドの型はCloneableインタフェースを実装しなければなりません。
例:
class MyClone implements Cloneable {
...
public Object clone() {
try {
MyClone myClone = (MyClone)super.clone();
//参照型のフィールドにもclone()
myClone.element = this.element.clone();
//参照型はListやMapの場合
myClone.listValue = new ArrayList();
for (Element ele:this.listValue) {
myClone.listValue.add(ele.clone());
}
return myClone;
} catch (CloneNotSupportedException e) {
return null;
}
}
...
}
//ElementクラスにもCloneableを実装させる
class Element implements Cloneable {
...
public Element clone() {
try {
return (Element)super.clone();
} catch (CloneNotSupportedException e) {
return null;
}
}
}
.
Sponsored Link
Comments
- Relative Articles
- Java開発及び実行環境の構築 | Linux篇 - (2008/08/31 20:34)
- Java開発及び実行環境の構築 | Windows篇 - (2008/08/31 21:18)
- 5分でJava Hello World! - (2008/08/31 22:13)
- Java言語の制御構文 - 条件分岐if/else/else if - (2008/10/01 21:30)
- Java言語の繰り返し制御構文 - for文 - (2008/10/03 18:22)
- Java言語の繰り返し制御構文 - do ... while文 - (2008/10/03 21:23)
- Java言語の繰り返し制御構文 - while文 - (2008/10/03 21:32)
- Java言語の基礎 - javacコマンドによるコンパイル - (2008/10/10 17:58)
- Java アクセス修飾子概要 - (2008/10/29 18:31)
- Javaアノテーション機能 概要 - (2009/02/12 13:06)