杰瑞科技汇

Java List如何深拷贝?

Of course! In Java, "cloning" a list can mean several different things. It's crucial to understand the difference because choosing the wrong method can lead to unexpected bugs in your application.

Java List如何深拷贝?-图1
(图片来源网络,侵删)

Here’s a complete guide covering the main ways to clone a Java list, from the simplest to the most robust.

Quick Answer: The Best Way (For Most Cases)

For a simple, shallow copy of a list, the most modern and concise way is to use the copyOf method introduced in Java 10.

import java.util.List;
import java.util.ArrayList;
public class QuickClone {
    public static void main(String[] args) {
        List<String> originalList = new ArrayList<>();
        originalList.add("Apple");
        originalList.add("Banana");
        // Create an unmodifiable shallow copy
        List<String> clonedList = List.copyOf(originalList);
        System.out.println("Original: " + originalList);
        System.out.println("Clone:    " + clonedList);
        // --- What happens if we modify the original? ---
        originalList.add("Cherry");
        System.out.println("\nAfter modifying original:");
        System.out.println("Original: " + originalList); // [Apple, Banana, Cherry]
        System.out.println("Clone:    " + clonedList);    // [Apple, Banana] -> Clone is unaffected
        // --- What happens if we try to modify the clone? ---
        // clonedList.add("Date"); // This will throw an UnsupportedOperationException!
    }
}

Key takeaway: List.copyOf() creates an unmodifiable shallow copy. This is often a good thing because it prevents accidental modification of the list from different parts of your code.


The Different Meanings of "Clone"

When you clone a list, you have two main considerations:

Java List如何深拷贝?-图2
(图片来源网络,侵删)
  1. Shallow Copy vs. Deep Copy:

    • Shallow Copy: Creates a new list object, but the elements inside the new list are the same references to the objects in the original list. If the objects are mutable (like ArrayList, StringBuilder, or your own custom classes), changes made to an object through the cloned list will be reflected in the original list.
    • Deep Copy: Creates a new list object and also creates copies of all the elements inside. The cloned list and the original list are completely independent. Modifying an object in one list will not affect the other.
  2. Modifiable vs. Unmodifiable Copy:

    • Modifiable: The new list can be changed (elements added, removed, or updated).
    • Unmodifiable: The new list is a read-only view. Any attempt to modify it will throw an UnsupportedOperationException.

Let's explore the methods based on these distinctions.


Method 1: Shallow Copy (The Classic Way)

This is the most common type of cloning. You create a new list and add all elements from the original to it.

Java List如何深拷贝?-图3
(图片来源网络,侵删)

A. Using the Copy Constructor (Most Common)

This is the most straightforward and recommended way to create a shallow, modifiable copy.

List<String> originalList = new ArrayList<>(Arrays.asList("A", "B", "C"));
List<String> shallowCopy = new ArrayList<>(originalList);
// Now you have two independent lists that can be modified.
shallowCopy.add("D");
System.out.println(originalList); // Output: [A, B, C]
System.out.println(shallowCopy);  // Output: [A, B, C, D]

How it works: The ArrayList constructor takes a Collection as an argument and creates a new ArrayList containing all of its elements.

B. Using addAll()

This is functionally identical to the copy constructor but can be more verbose.

List<String> originalList = new ArrayList<>(Arrays.asList("A", "B", "C"));
List<String> shallowCopy = new ArrayList<>();
shallowCopy.addAll(originalList);
// Result is the same as above

C. Using Java 8 Streams

A modern, functional approach.

List<String> originalList = new ArrayList<>(Arrays.asList("A", "B", "C"));
List<String> shallowCopy = originalList.stream()
                                       .collect(Collectors.toCollection(ArrayList::new));
// Result is the same as above

D. Using clone() (Not Recommended!)

The clone() method exists on the Object class and is implemented by ArrayList. However, it has several issues:

  • It returns an Object, so you must cast it.
  • It's protected in the AbstractList class, so you can't call it on a list reference that is of type List.
  • The behavior can be unpredictable for custom list implementations.

Avoid clone() unless you are working with a legacy codebase that forces you to. It's considered a "defective" feature of Java.

// Don't do this!
List<String> originalList = new ArrayList<>(Arrays.asList("A", "B", "C"));
// @SuppressWarnings("unchecked") is often needed
@SuppressWarnings("unchecked")
List<String> badCopy = (List<String>) originalList.clone(); 

Method 2: Deep Copy (The "Real" Clone)

A deep copy is only necessary if your list contains mutable objects and you need the lists to be completely independent.

Let's say you have a Person class.

class Person {
    private String name;
    public Person(String name) { this.name = name; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    @Override public String toString() { return name; }
}

Now, let's see the difference between a shallow and deep copy.

Shallow Copy Example (The Problem)

List<Person> originalPeople = new ArrayList<>();
originalPeople.add(new Person("Alice"));
originalPeople.add(new Person("Bob"));
// Create a shallow copy
List<Person> shallowCopy = new ArrayList<>(originalPeople);
// Modify an object through the shallow copy
shallowCopy.get(0).setName("Alicia");
// The change is reflected in the original list!
System.out.println("Original: " + originalPeople.get(0)); // Output: Alicia
System.out.println("Clone:    " + shallowCopy.get(0));    // Output: Alicia

Deep Copy Solutions

There is no single built-in Java method for deep cloning. You have to implement it yourself.

Solution 1: Manual Deep Copy (Recommended)

Iterate through the original list and create new copies of each element.

List<Person> originalPeople = new ArrayList<>();
originalPeople.add(new Person("Alice"));
originalPeople.add(new Person("Bob"));
List<Person> deepCopy = new ArrayList<>();
for (Person originalPerson : originalPeople) {
    // Create a new Person object with the same name
    deepCopy.add(new Person(originalPerson.getName()));
}
// Now they are truly independent
deepCopy.get(0).setName("Alicia");
System.out.println("Original: " + originalPeople.get(0)); // Output: Alice
System.out.println("Deep Copy: " + deepCopy.get(0));    // Output: Alicia

Best for: When you have full control over the object types in your list and their copying logic is simple.

Solution 2: Serialization (The "Hack")

If your objects implement the Serializable interface, you can serialize the list to a byte stream and then deserialize it back into a new list. This creates a perfect deep copy.

import java.io.*;
public class DeepCopyUtil {
    @SuppressWarnings("unchecked")
    public static <T extends Serializable> List<T> deepCopyList(List<T> originalList) {
        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(originalList);
            oos.flush();
            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            return (List<T>) ois.readObject();
        } catch (IOException | ClassNotFoundException e) {
            throw new RuntimeException("Failed to deep copy list", e);
        }
    }
}

How to use it:

  1. Make sure your Person class implements Serializable.
    import java.io.Serializable;
    class Person implements Serializable { ... }
  2. Use the utility method.
    List<Person> originalPeople = ...;
    List<Person> deepCopy = DeepCopyUtil.deepCopyList(originalPeople);

Caveats:

  • Slow: Involves I/O operations and is much slower than manual copying.
  • Brittle: Fails if any object in the list or its object graph is not Serializable.
  • Not always a true deep copy: It performs a "deep serialization copy," which is usually what you want, but it's good to be aware of the distinction.

Summary Table

Method Type of Copy Modifiable When to Use
new ArrayList<>(original) Shallow Yes Most common use case. When you need a new, independent list that can be changed, but the elements are immutable (like String, Integer) or you don't mind shared references.
List.copyOf(original) Shallow No When you need an immutable, thread-safe snapshot of a list. Perfect for returning from methods to prevent external modification.
Manual Loop Deep Yes When you have mutable objects and need the lists to be completely independent. The most robust and performant way.
Serialization Deep Yes As a last resort or for complex object graphs when you can't or don't want to write manual copy logic. Be aware of performance and Serializable requirements.
分享:
扫描分享到社交APP
上一篇
下一篇