ともさんのHP >プロブラミング >自作JavaLibrary >複雑な参照型オブジェクトのディープコピー
【Java】複雑な参照型オブジェクトのディープコピー
なぞなぞみたいだけど、この方法を見つけるのに苦労しました。
ネットを探してもあまり情報がないので、ネット上に放流しておきます。
配列や参照型のシャローコピーとディープコピー
この方法はよく知られているので、改めて書くほどのものではない。
参照型オブジェクトを人に置き換えて考えてみる。
シャローコピーは一人の人に別名を付けるようなもの。
ある人を、子供はお父さんと名前を付け、店の人はお客さんと呼ぶ。会社に行けば課長さん。
沢山人がいるようだが、実際には一人の人。課長が過労で倒れれば、お父さんもお客さんも過労で倒れる。
お客さんがお金を払えば、お父さんの財布も課長の財布も、みんな同じ額だけ減る。
ディープコピーはクローン人間を作るようなもの。名前は違うだけで、中身はまったく同じ人を作る。
最初は見分けがつかないけど、別人なので、やがて違いが出てくる。片方が勉強しても、もう片方は頭がよくなるわけではない。
片方が死んでしまっても、もう片方に影響はない。
参照型オブジェクトのシャローコピーをJavaで書くと以下の通り。
public class Hito {
String atama = "1";
int te = 1;
Naizou naizou = new Naizou();
public static void main( String[] s)
{
Hito hito = new Hito();
System.out.println("頭:"+hito.atama+" 手:" + hito.te+" 胃:" + hito.naizou.i+" 腸:" +hito.naizou.tyou );
Hito hito2= hito;
hito2.atama = "2";
hito2.te = 2;
hito2.naizou.i = "2";
hito2.naizou.tyou =2;
System.out.println("頭:"+hito.atama+" 手:" + hito.te+" 胃:" + hito.naizou.i+" 腸:" +hito.naizou.tyou );
}
}
public class Naizou {
String i = "1";
int tyou = 1;
}
実行結果
頭:1 手:1 胃:1 腸:1
頭:2 手:2 胃:2 腸:2
参照型のフィールドを持つ参照型オブジェクトのディープコピー
再び人間に例えてみる。内臓という参照型オブジェクトを持つ人の場合、単にディープコピーすると、変なクローン人間が出来てしまう。
人間は別なのに、同一の内臓を共有することになってしまうのだ。
別人だから、一方が手にけがをしても、他方に影響はしない。だが、一方がお腹を壊せば、必ずもう一方もお腹を壊す...同じ内臓ですから。
Java
楽天 で書くとこんな感じ。
public class Hito implements Cloneable {
String atama = "1";
int te = 1;
Naizou naizou = new Naizou();
public static void main( String[] s)
{
Hito hito = new Hito();
System.out.println("頭:"+hito.atama+" 手:" + hito.te+" 胃:" + hito.naizou.i+" 腸:" +hito.naizou.tyou );
Hito hito2= hito.clone();
hito2.atama = "2";
hito2.te = 2;
hito2.naizou.i = "2";
hito2.naizou.tyou = 2;
System.out.println("頭:"+hito.atama+" 手:" + hito.te+" 胃:" + hito.naizou.i+" 腸:" +hito.naizou.tyou );
}
//クローン作成
public Hito clone(){
try {
return (Hito)super.clone();
} catch (CloneNotSupportedException e) {throw new InternalError(e.toString());}
}
}
実行結果
頭:1 手:1 胃:1 腸:1
頭:1 手:1 胃:2 腸:2
オブジェクト全体をディープコピーする方法
内臓もクローンするには、内臓もクローンしますと書かなければやってくれない。
ここが判らなくて苦労した。
オブジェクト全体をディープコピーするには下記のように書き換えます。
public class Hito implements Cloneable {
String atama = "1";
int te = 1;
Naizou naizou = new Naizou();
public static void main( String[] s)
{
Hito hito = new Hito();
System.out.println("頭:"+hito.atama+" 手:" + hito.te+" 胃:" + hito.naizou.i+" 腸:" +hito.naizou.tyou );
Hito hito2= hito.clone();
hito2.atama = "2";
hito2.te = 2;
hito2.naizou.i = "2";
hito2.naizou.tyou = 2;
System.out.println("頭:"+hito.atama+" 手:" + hito.te+" 胃:" + hito.naizou.i+" 腸:" +hito.naizou.tyou );
}
//クローン作成
public Hito clone(){
try {
Hito hito = (Hito)super.clone();
hito.naizou = this.naizou.clone();
return (Hito) hito ;
} catch (CloneNotSupportedException e) {throw new InternalError(e.toString());}
}
}
public class Naizou implements Cloneable {
String i = "1";
int tyou = 1;
//クローン作成
public Naizou clone(){
try {
return (Naizou)super.clone();
} catch (CloneNotSupportedException e) {throw new InternalError(e.toString());}
}
}
実行結果
頭:1 手:1 胃:1 腸:1
頭:1 手:1 胃:1 腸:1
参照型の配列のフィールドを持つ参照型オブジェクトの配列をディープコピー
最後に内臓を配列にして、ついでに人間も配列にしてディープコピーしてみる。
人の配列はあまり意味がないけど、内臓の配列はちょっとややこしくなった。
もっとスマートに記述する方法があると思うけど、とりあえず目標は達成したのでこれで完成。
public class Hito implements Cloneable {
String atama ;
int te ;
Naizou[] naizou = new Naizou[2];
public static void main( String[] s)
{
//hito配列に中身を入れる
Hito[] hito = new Hito[2];
for(int i=0;i<hito.length;i++) {
hito[i]= new Hito();
hito[i].atama = "1" + I;
hito[i].te = 10 + I;
for(int ii=0;ii<hito[i].naizou.length;ii++) {
hito[i].naizou[ii]= new Naizou();
hito[i].naizou[ii].i = "1" + ii;
hito[i].naizou[ii].tyou = 10 + ii;
}}
//hitoの中身
for(int i=0;i<hito.length;i++) {
for(int ii=0;ii<hito[i].naizou.length;ii++) {
System.out.println("頭:"+hito[i].atama+" 手:" + hito[i].te+" 胃:" + hito[i].naizou[ii].i+" 腸:" +hito[i].naizou[ii].tyou );
}
}
//ディープコピー
Hito[] hito2 = new Hito[ hito.length ];
for(int i=0;i<hito2.length;i++) {
hito2[i]= hito[i].clone();
}
//hito2の中身を入れ替える
for(int i=0;i<hito2.length;i++) {
hito2[i].atama = "2" + I;
hito2[i].te = 20 + I;
for(int ii=0;ii<hito2[i].naizou.length;ii++) {
hito2[i].naizou[ii].i = "2" + ii;
hito2[i].naizou[ii].tyou = 20 + ii;
}}
//hitoの中身
for(int i=0;i<hito.length;i++) {
for(int ii=0;ii<hito[i].naizou.length;ii++) {
System.out.println("頭:"+hito[i].atama+" 手:" + hito[i].te+" 胃:" + hito[i].naizou[ii].i+" 腸:" +hito[i].naizou[ii].tyou );
}
}
}
//クローン作成
public Hito clone(){
try {
Hito hito = (Hito)super.clone();
hito.naizou = this.naizou.clone();
for(int i=0;i<hito.naizou.length;i++){
hito.naizou[i] = this.naizou[i].clone();
}
return (Hito) hito ;
} catch (CloneNotSupportedException e) {throw new InternalError(e.toString());}
}
}
実行結果
頭:10 手:10 胃:10 腸:10
頭:10 手:10 胃:11 腸:11
頭:11 手:11 胃:10 腸:10
頭:11 手:11 胃:11 腸:11
頭:10 手:10 胃:10 腸:10
頭:10 手:10 胃:11 腸:11
頭:11 手:11 胃:10 腸:10
頭:11 手:11 胃:11 腸:11
最終更新日: 2014-12-03 05:53:21
ともさんのHP >プロブラミング >自作JavaLibrary >複雑な参照型オブジェクトのディープコピー
ツイート