ともさんの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 >複雑な参照型オブジェクトのディープコピー

このエントリーをはてなブックマークに追加
広告
おすすめ記事
新着ページ

須屋  
3Dシーンに2Dコントロールを表示  
井戸ポンプ  
ファイル選択ダイアログ  
UcanAccess導入覚書  
ともさんのHP  
タイムラプス撮影  
Raspberry Piを太陽電池に接続  
ラズパイ(Raspberry Pi)で電子工作  
茶室の平面図集  
ともさんの開発室  

私の他のサイト

ともさんの箱庭(ブログ)
家庭菜園
3D-CAD
洋裁CAD

いいねなど

 RSS 
PageSpeedInsights
html5チェック

Author: Tomoyuki Ito

このサイトの文章・写真の無断転載を禁じます