您的位置:

Javalist深拷贝探究

一、为什么需要深拷贝

对于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深拷贝的有效方式。