一、为什么需要深拷贝
对于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<Student> studentList = new ArrayList<>();
studentList.add(new Student(1, "张三"));
studentList.add(new Student(2, "李四"));
//使用copy方法进行浅拷贝
List<Student> copyList = new ArrayList<Student>(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<Student> 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<Student> 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<Student> 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<Student>) ois.readObject();//反序列化
} catch (Exception e) {
e.printStackTrace();
}
四、小结
在需要进行列表复制时,我们有时会遇到浅拷贝会带来的问题,因此需要使用深拷贝来解决这个问题。Java8的stream流、BeanUtils以及序列化都是实现Javalist深拷贝的有效方式。