今天學習ArrayList
一,淺複制
它的複制構造函數和clone()函數均為淺複制(shallow copy),即複制對象的引用。相反的深複制,則是複制對象的本身。
1,複制構造函數 ArrayList (Collection<? extends E> c)
得到了一個新的ArrayList對象,它包含了對c中元素的複制。但是注意,集合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"));
temp2中第0個元素引用的StringBuffer對象修改為“yesno”,它和temp1中第0個元素引用的是同一個StringBuffer對象 ,即是兩個元素指向的還是同一記憶體。是以temp1和temp2均儲存了“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對象,該對象中的内容就不能改變了。是以第二段程式中temp2的第0個元素其實是指向了新的String對象“no”,而temp1的第0個元素仍然引用“yes”
3, java.util.list.addAll()方法同樣是淺複制
4,下邊這個周遊list逐個元素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的使用
文章稱之為深拷貝,說兩個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的大小才變化。
是以因為des1.size()為0;3表示的是這個List的容納能力為3,并不是說des1中就有了3個元素。然而進行copy()時候,首先做的是将desc1的size和src1的size大小進行比較,隻有當desc1的 size 大于或者等于src1的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();
則是temp2複制了temp1中的元素,這些元素是引用變量,被複制的元素和原本的元素指向同一記憶體。對temp1中元素引用的對象的修改會影響到temp2中元素引用的對象;若隻是将temp1中元素重新引用新的對象,指向新的記憶體,則temp2中元素仍然引用原本的對象、指向原本的記憶體。