一、為什麼需要深拷貝
對於Javalist,常常需要複製一個列表的內容到另外一個位置,這時候我們通常會使用List接口提供的copy方法進行複製。但是,使用copy方法複製出來的列表只是原列表的淺拷貝,即原列表和複製後的列表指向同一個內存地址,修改一個列表也會同時修改另一個列表。因此,在某些情況下,我們需要使用深拷貝來避免這種問題。
假設我們有一個List列表,其中存儲的是自定義的Student對象,我們需要將該列表中的所有Student對象複製一份到另一個列表中。 如果直接使用copy方法,只是對Student對象進行了淺拷貝,如果我們在後面修改一個列表中的Student對象屬性值,則另一個列表中的同一個對象的屬性值也會同時修改,這樣就會產生很多難以預測的問題。而使用深拷貝,則可以解決這個問題。
/**
* 聲明Student類,包含一個int類型的id屬性以及一個String類型的name屬性
*/
private static class Student{
private int id;
private String name;
//構造函數
public Student(int id, String name) {
this.id = id;
this.name = name;
}
//getter和setter方法
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public static void main(String[] args){
//創建一個List存儲Student對象
List studentList = new ArrayList();
studentList.add(new Student(1, "張三"));
studentList.add(new Student(2, "李四"));
//使用copy方法進行淺拷貝
List copyList = new ArrayList(studentList);
//修改原列表第一個元素的name值
studentList.get(0).setName("王五");
System.out.println(studentList.get(0).getName());//輸出:王五
System.out.println(copyList.get(0).getName());//輸出:王五
}
二、淺拷貝與深拷貝的區別
從例子中可以看出,使用copy方法只是對Student對象進行了淺拷貝。那麼什麼是淺拷貝,什麼是深拷貝呢?
淺拷貝指的是拷貝一個對象,實際上只是拷貝了對象的引用(地址),新對象和原對象指向同一個內存地址。這樣,在操作新對象時,原對象也會同時受到影響。而深拷貝則是將原對象的所有屬性都複製到新對象中,並在堆內存中開闢新的空間存放新對象,新對象和原對象互不影響。
三、實現Javalist深拷貝
Javalist的深拷貝,可以通過以下幾種方式:
1. Java8的stream流實現深拷貝
使用Java8新特性的stream流實現深拷貝,需要注意每個類需要實現接口Serializable,以便對象序列化。
//方法1:使用Java8的stream流實現深拷貝
List copyList1 = studentList.stream().map(student -> {
Student s = new Student(student.getId(), student.getName());
return s;
}).collect(Collectors.toList());
2. 使用BeanUtils實現深拷貝
使用Apache Commons BeanUtils提供的BeanUtils.copyProperties()方法實現深拷貝,需要注意每個類需實現接口Cloneable。
//方法2:使用BeanUtils實現深拷貝
List copyList2 = new ArrayList();
try {
for (Student student : studentList) {
Student newStudent = (Student) BeanUtils.cloneBean(student);
copyList2.add(newStudent);
}
} catch (Exception e) {
e.printStackTrace();
}
3. 使用序列化公共方法實現深拷貝
將對象序列化後再進行反序列化即可實現深拷貝,需要注意每個類必須實現Serializable接口。
//方法3:使用序列化公共方法實現深拷貝
List copyList3 = new ArrayList();
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();//輸出流
ObjectOutputStream oos = new ObjectOutputStream(bos);//對象輸出流
oos.writeObject(studentList);//序列化
oos.flush();
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());//輸入流
ObjectInputStream ois = new ObjectInputStream(bis);//對象輸入流
copyList3 = (List) ois.readObject();//反序列化
} catch (Exception e) {
e.printStackTrace();
}
四、小結
在需要進行列表複製時,我們有時會遇到淺拷貝會帶來的問題,因此需要使用深拷貝來解決這個問題。Java8的stream流、BeanUtils以及序列化都是實現Javalist深拷貝的有效方式。
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hk/n/185324.html
微信掃一掃
支付寶掃一掃