您的位置:

Java中实现List深度拷贝的方法

一、什么是深度拷贝

在Java中,当我们需要复制一个对象的属性到另一个对象时,常常会选择使用浅拷贝。浅拷贝是指将对象的引用进行复制,这意味着新对象与原对象使用的是同一个内存地址,所以改变新对象中的属性值也会影响到原对象的属性值。相反,深度拷贝是指将对象包含的所有子对象也进行拷贝,这样新对象与原对象是完全独立的,它们的属性值互不影响。

二、使用clone方法实现List深度拷贝

在Java中,Object类实现了一个叫做clone的方法,可以用来创建对象的副本。如果一个类想要使用clone方法,必须实现Cloneable接口,这个接口是一个标记接口,它并没有需要实现的方法。

首先,我们需要定义一个可拷贝的类,例如:

public class Person implements Cloneable {
    private String name;
    private int age;
    private List hobbies; // 需要进行深度拷贝的属性

    public Person(String name, int age, List
    hobbies) {
        this.name = name;
        this.age = age;
        this.hobbies = hobbies;
    }

    // 省略getter和setter方法

    @Override
    protected Object clone() throws CloneNotSupportedException {
        // 先调用父类的clone方法获得一个浅拷贝的Person对象
        Person clone = (Person) super.clone();
        // 再将hobbies属性进行深度拷贝
        clone.hobbies = new ArrayList<>(hobbies);
        return clone;
    }
}

   
  

在上面的代码中,我们先调用父类的clone方法获得一个浅拷贝的Person对象,然后将hobbies属性进行深度拷贝,最后返回新的Person对象。

接下来,我们可以测试一下这个深度拷贝方法是否可用:

public static void main(String[] args) throws CloneNotSupportedException {
    List originalList = new ArrayList<>();
    originalList.add(new Person("Tom", 20, Arrays.asList("swimming", "reading")));
    originalList.add(new Person("Jerry", 18, Arrays.asList("music", "gaming")));

    List
    clonedList = new ArrayList<>();
    for (Person person : originalList) {
        clonedList.add((Person) person.clone());
    }

    // 改变hobbies属性值
    clonedList.get(0).getHobbies().add("hiking");

    System.out.println(originalList.get(0).getHobbies()); // [swimming, reading]
    System.out.println(clonedList.get(0).getHobbies()); // [swimming, reading, hiking]
}

   
  

可以看到,原列表和克隆列表中的hobbies属性值值发生了变化,证明深度拷贝方法生效。

三、使用序列化实现List深度拷贝

除了使用clone方法实现深度拷贝外,我们还可以使用Java的序列化机制。序列化是将对象转换成字节序列的过程,而反序列化则是将字节序列转换回对象。如果我们将一个对象通过序列化后再反序列化回来,得到的对象就是原对象的一个深度拷贝。

同样地,我们需要定义一个可序列化的类:

public class Person implements Serializable {
    private String name;
    private int age;
    private List hobbies; // 需要进行深度拷贝的属性

    public Person(String name, int age, List
    hobbies) {
        this.name = name;
        this.age = age;
        this.hobbies = hobbies;
    }

    // 省略getter和setter方法
}

   
  

下面是序列化和反序列化的代码:

public static void main(String[] args) throws IOException, ClassNotFoundException {
    List originalList = new ArrayList<>();
    originalList.add(new Person("Tom", 20, Arrays.asList("swimming", "reading")));
    originalList.add(new Person("Jerry", 18, Arrays.asList("music", "gaming")));

    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
    objectOutputStream.writeObject(originalList);

    ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
    ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
    List
    clonedList = (List
    ) objectInputStream.readObject();

    // 改变hobbies属性值
    clonedList.get(0).getHobbies().add("hiking");

    System.out.println(originalList.get(0).getHobbies()); // [swimming, reading]
    System.out.println(clonedList.get(0).getHobbies()); // [swimming, reading, hiking]
}

    
   
  

可以看到,经过序列化和反序列化后,原列表和克隆列表中的hobbies属性值也发生了变化,证明深度拷贝方法生效。

四、使用Apache Commons Lang实现List深度拷贝

如果我们不想自己实现深度拷贝的方法,可以使用Apache Commons Lang库中的SerializationUtils类来实现。这个类提供了一组静态方法,可以对任何实现了Serializable接口的对象进行深度拷贝。

接下来看看如何使用这个类:

public static void main(String[] args) throws IOException, ClassNotFoundException {
    List originalList = new ArrayList<>();
    originalList.add(new Person("Tom", 20, Arrays.asList("swimming", "reading")));
    originalList.add(new Person("Jerry", 18, Arrays.asList("music", "gaming")));

    List
    clonedList = (List
    ) SerializationUtils.clone((Serializable) originalList);

    // 改变hobbies属性值
    clonedList.get(0).getHobbies().add("hiking");

    System.out.println(originalList.get(0).getHobbies()); // [swimming, reading]
    System.out.println(clonedList.get(0).getHobbies()); // [swimming, reading, hiking]
}

    
   
  

可以看到,使用SerializationUtils类的clone方法同样可以实现深度拷贝,且更加方便。

五、总结

本文介绍了Java中三种实现List深度拷贝的方法:使用clone方法、使用序列化、使用Apache Commons Lang库的SerializationUtils类。在使用这些方法时,需要注意对象中是否包含子对象,需要对子对象也进行深度拷贝。