一、為什麼需要深拷貝
對於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