Java中的集合及深拷贝与浅拷贝
Java是一种面向对象的编程语言,其中集合是常用的数据结构之一,具有方便快捷的特点。在Java开发中,我们常常需要对集合进行复制(拷贝)操作。但是,拷贝操作并不是简单的复制,而应该分为浅拷贝和深拷贝两种不同的方式,本文将分别介绍Java中集合的浅拷贝和深拷贝实现方法。
集合简介
Java中的集合是一种用于存储对象的容器,其中包括List、Set、Map等多种类型。集合的特点是可以动态地增加、删除和修改其中的元素。Java中的集合框架分为两个接口:Collection和Map,其中Collection是所有集合接口的根接口,其常用的子接口包括List、Set和Queue。Map接口表示键-值映射,常用的实现类有HashMap和TreeMap。
浅拷贝
浅拷贝是指拷贝对象时,只复制对象的引用而不是对象本身。这意味着拷贝后的对象与原始对象共享同一个内存地址,对于引用类型的成员变量,拷贝后的对象和原始对象都指向同一个实例。
在Java中,可以通过调用Object类的clone()方法实现浅拷贝。该方法会复制对象的字段,但是对于引用类型的字段,仅复制其引用,而不会复制引用指向的对象。
下面是一个示例代码,演示了如何使用clone()方法实现集合的浅拷贝。
import java.util.ArrayList;import java.util.List;class Person implements Cloneable { private String name; private int age; private List<String> hobbies; public Person(String name, int age, List<String> hobbies) { this.name = name; this.age = age; this.hobbies = hobbies; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public List<String> getHobbies() { return hobbies; } public void setHobbies(List<String> hobbies) { this.hobbies = hobbies; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); }}public class ShallowCopyDemo { public static void main(String[] args) throws CloneNotSupportedException { List<Person> list1 = new ArrayList<>(); List<String> hobbies = new ArrayList<>(); hobbies.add("reading"); hobbies.add("swimming"); Person person1 = new Person("Tom", 20, hobbies); list1.add(person1); List<Person> list2 = (List<Person>) ((ArrayList<Person>) list1).clone(); System.out.println(list1 == list2); System.out.println(list1.get(0) == list2.get(0)); System.out.println(list1.get(0).getHobbies() == list2.get(0).getHobbies()); }}
在上面的示例代码中,我们创建了一个Person类,其中包含一个List类型的成员变量hobbies。然后,我们创建了一个List类型的集合list1,并向其中添加一个Person对象person1。接着,我们通过调用list1的clone()方法创建了一个新的集合list2,然后比较list1和list2、person1和list2中的Person对象以及person1和list2中的Person对象的hobbies是否相同。可以看到,由于是浅拷贝,list1和list2、person1和list2中的Person对象及其hobbies都是共享同一块内存空间的。
深拷贝
与浅拷贝不同,深拷贝是指拷贝对象时,不仅复制对象的引用,而且复制引用指向的对象,即创建一个全新的对象,并将原始对象中的所有字段值复制到新对象中。这样做的好处是,新对象和原始对象之间不存在任何关联,对新对象的修改不会影响原始对象。
在Java中,实现深拷贝的方法有多种,下面介绍两种常用的方式。
1. 序列化与反序列化
Java中的序列化可以将对象转换为字节流,而反序列化则可以将字节流还原为对象。利用这一特性,我们可以通过将对象序列化为字节流,然后再反序列化成新对象的方式实现深拷贝。
下面是一个示例代码,演示了如何使用序列化和反序列化实现集合的深拷贝。
import java.io.*;import java.util.ArrayList;import java.util.List;class Person implements Serializable { private String name; private int age; private List<String> hobbies; public Person(String name, int age, List<String> hobbies) { this.name = name; this.age = age; this.hobbies = hobbies; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public List<String> getHobbies() { return hobbies; } public void setHobbies(List<String> hobbies) { this.hobbies = hobbies; }}public class DeepCopyDemo { public static void main(String[] args) throws IOException, ClassNotFoundException { List<Person> list1 = new ArrayList<>(); List<String> hobbies = new ArrayList<>(); hobbies.add("reading"); hobbies.add("swimming"); Person person1 = new Person("Tom", 20, hobbies); list1.add(person1); ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(list1); oos.flush(); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); List<Person> list2 = (List<Person>) ois.readObject(); System.out.println(list1 == list2); System.out.println(list1.get(0) == list2.get(0)); System.out.println(list1.get(0).getHobbies() == list2.get(0).getHobbies()); }}
在上面的示例代码中,我们创建了一个Person类,其中包含一个List类型的成员变量hobbies。然后,我们创建了一个List类型的集合list1,并向其中添加一个Person对象person1。接着,我们通过将list1序列化为字节流,然后再将字节流反序列化为新的List对象list2,实现了深拷贝。最后,我们比较list1和list2、person1和list2中的Person对象以及person1和list2中的Person对象的hobbies是否相同。可以看到,由于是深拷贝,list1和list2、person1和list2中的Person对象及其hobbies均不是共享同一块内存空间的。
2. 手动递归拷贝
手动递归拷贝是另一种实现深拷贝的方式,其思路是先创建一个空的新对象,然后递归地复制原始对象中的所有字段值到新对象中。对于引用类型的字段,需要继续递归拷贝其指向的对象,直到所有字段值均被复制到新对象中。
下面是一个示例代码,演示了如何使用手动递归拷贝实现集合的深拷贝。
import java.util.ArrayList;import java.util.List;class Person { private String name; private int age; private List<String> hobbies; public Person(String name, int age, List<String> hobbies) { this.name = name; this.age = age; this.hobbies = hobbies; } public String getName() { return name; } publicvoid setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public List<String> getHobbies() { return hobbies; } public void setHobbies(List<String> hobbies) { this.hobbies = hobbies; } public Person deepCopy() { List<String> newHobbies = new ArrayList<>(); for (String hobby : hobbies) { newHobbies.add(hobby); } return new Person(name, age, newHobbies); }}public class DeepCopyDemo2 { public static void main(String[] args) { List<Person> list1 = new ArrayList<>(); List<String> hobbies = new ArrayList<>(); hobbies.add("reading"); hobbies.add("swimming"); Person person1 = new Person("Tom", 20, hobbies); list1.add(person1); List<Person> list2 = new ArrayList<>(); for (Person person : list1) { list2.add(person.deepCopy()); } System.out.println(list1 == list2); System.out.println(list1.get(0) == list2.get(0)); System.out.println(list1.get(0).getHobbies() == list2.get(0).getHobbies()); }}
在上面的示例代码中,我们创建了一个Person类,其中包含一个List类型的成员变量hobbies。然后,我们创建了一个List类型的集合list1,并向其中添加一个Person对象person1。接着,我们通过手动递归拷贝的方式,创建了一个新的集合list2,并将list1中的所有Person对象递归拷贝到list2中。最后,我们比较list1和list2、person1和list2中的Person对象以及person1和list2中的Person对象的hobbies是否相同。可以看到,由于是深拷贝,list1和list2、person1和list2中的Person对象及其hobbies均不是共享同一块内存空间的。
总结
在Java中,集合是一种常用的数据结构,而集合的拷贝操作又分为浅拷贝和深拷贝两种方式。浅拷贝只复制对象的引用,而深拷贝则复制引用指向的对象,创建一个全新的对象。实现浅拷贝可以通过调用Object类的clone()方法或者手动复制对象的字段实现,而实现深拷贝可以通过序列化与反序列化或者手动递归拷贝实现。在实际开发中,我们应该根据具体情况选择合适的拷贝方式,以保证代码的正确性和性能。
来源地址:https://blog.csdn.net/stormjun/article/details/131728657