今天学习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>();注意:必须用Arrays.asList方法
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);
public static <T> List<T> asList(T... a)返回一个受指定数组支持的固定大小的列表。
如果如下边这样写会抛出数组越界异常
List des1 = new ArrayList( 3 );
Collections.copy(des1,src1);
所以因为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中元素仍然引用原本的对象、指向原本的内存。