『データ構造とJava集合フレームワーク第3版』読書ノート(五)浅複製(shallow copy)と深複製


今日はArrayListを勉強します
一、浅いコピー
そのレプリケーションコンストラクション関数とclone()関数は、いずれも浅いレプリケーション(shallow copy)、すなわちレプリケーションオブジェクトの参照です.反対に深いレプリケーションは、レプリケーションオブジェクト自体です.
1,複製構造関数ArrayList(Collectionc)
c中の要素のコピーを含む新しいArrayListオブジェクトが得られた.ただし、集合cの要素の本質は、オブジェクトではなく参照であることに注意してください.参照されたオブジェクト自体はコピーされていません.
2,clone()関数もそうです
例:
ArrayList<StringBuffer> temp1=new ArrayList<StringBuffer>();
StringBuffer x=new StringBuffer("yes");
temp1.add(x);
ArrayList<StringBuffer> temp2=(ArrayList)temp1.clone();
temp2.set(0,x.append("no"));

temp 2の0番目のエレメント参照StringBufferオブジェクトは、temp 1の0番目のエレメント参照と同じStringBufferオブジェクトに変更されました.
つまり、2つの要素が同じメモリを指しているのか、同じメモリを指しているのか.だからtemp 1もtemp 2も「yesno」を保存しました
でも
ArrayList<String> temp1=new ArrayList();
String x="yes";temp1.add(x);ArrayList<String>  temp2=(ArrayList)temp1.clone();
temp2.set(0,"no");

StringBufferオブジェクトは可変ですが、Stringオブジェクトは可変ではありません.Stringオブジェクトが作成されると、そのオブジェクトの内容は変更できません.したがって、第2セグメントプログラムのtemp 2の0番目の要素は、実際には新しいStringオブジェクト「no」を指し、temp 1の0番目の要素は「yes」を参照する.
3, java.util.list.addAll()メソッドも同様に浅いレプリケーションです
4,下のこのlistを1つの要素addごとに遍歴する方法も浅いコピーで、これは比較的に間違いやすいです
class Person implements Serializable{
	private int age;
	private String name;
	
	public Person(){};
	public Person(int age,String name){
		this.age=age;
		this.name=name;
	}
	
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
	public String toString(){
		return this.name+"-->"+this.age;
	}
	
}
List<Person> destList=new ArrayList<Person>(srcList.size());
for(Person p : srcList){
	destList.add(p);
}
		
printList(destList);
srcList.get(0).setAge(100);
printList(destList);

s
rcListの0番目の要素に格納されるPersonオブジェクトの値は変更可能である.StringBufferに似ています
5,Collectionsのcopy(List desc,List src)メソッドも浅いコピーです.下の文章の言い方はjavaの中で深くコピーして伸ばすCollections.copyの使用を誤っています
文章は深いコピーと呼ばれ、2つのlistの各要素が同じメモリではないことを示しています.コードは次のとおりです.
List<String> src = new ArrayList<String>();
  src.add("111");
  src.add("222");
  src.add("333");
  src.add("444");
  List<String> dic = new ArrayList<String>(Arrays.asList(new String[src
    .size()]));
  Collections.copy(dic, src);
注意:Arraysを使用する必要があります.asListメソッド
public static <T> List<T> asList(T... a)

下のように書くと配列境界異常が放出されます
List des1 = new  ArrayList( 3 );
Collections.copy(des1,src1);

ArrayListのcapacity(収容能力サイズ)は指定(最適指定)できますが、初期化時のsizeのサイズは常に0にデフォルト設定され、addやremoveなどの関連操作を行う場合にのみsizeのサイズが変化します.
だからdes 1.size()は0です.3はこのリストの収容能力が3であることを示し,des 1に3つの要素があるわけではない.しかし、copy()を行う場合、まずdesc 1のsizeとsrc 1のsizeサイズを比較し、desc 1のsizeがsrc 1のsizeより大きいか等しい場合にのみコピーを行い、そうでない場合はIndexOutOfBoundsException異常を投げ出す.
実際の状況:
まだ浅いレプリケーションです.この文書の著者を誤導したのはStringオブジェクトが変更できないためであり,clone法に倣って例を挙げるとStringBufferに変更すると実質的に浅い複製であることがわかる.
二、深く複製する方法
シーケンス化
public static <T> List<T> deepCopy(List<T> src) throws IOException, ClassNotFoundException {
	ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
	ObjectOutputStream out = new ObjectOutputStream(byteOut);
	out.writeObject(src);

	ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
	ObjectInputStream in = new ObjectInputStream(byteIn);
	@SuppressWarnings("unchecked")
	List<T> dest = (List<T>) in.readObject();
	return dest;
}

参考文章:頼りないjava.util.Listディープコピー方法
三、浅いレプリケーションと別名は違います.
エイリアスは、同じオブジェクトに対する別の参照変数です.例えばArrayList sameList=myList;
sameListとmyListは同じArrayListオブジェクトの参照変数であり、myListオブジェクトへの変更、すなわちsameListオブジェクトへの変更である.
上記の浅い複製方法は、
ArrayList<E> temp2=(ArrayList)temp1.clone();

はtemp 2がtemp 1の要素をコピーし、これらの要素は参照変数であり、コピーされた要素は元の要素と同じメモリを指します.temp 1の要素参照オブジェクトの変更はtemp 2の要素参照オブジェクトに影響します.temp 1の要素を新しいオブジェクトに再参照し、新しいメモリを指すだけで、temp 2の要素は元のオブジェクトを参照し、元のメモリを指します.