杰瑞科技汇

Java中如何高效复制List?

Of course! In Java, "copying a list" can mean several different things, and it's crucial to choose the right method for your specific needs. The best method depends on whether you need a shallow copy or a deep copy, and whether the new list should be modifiable.

Let's break down the most common ways to copy a List in Java.

The Simplest Way: Copy Constructor (Shallow Copy)

This is the most straightforward method. You create a new list and pass the original list to its constructor.

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class CopyListExample {
    public static void main(String[] args) {
        // Original list of mutable objects
        List<StringBuilder> originalList = new ArrayList<>(Arrays.asList(
            new StringBuilder("Apple"),
            new StringBuilder("Banana"),
            new StringBuilder("Cherry")
        ));
        // Create a shallow copy using the copy constructor
        List<StringBuilder> copiedList = new ArrayList<>(originalList);
        System.out.println("Original List: " + originalList);
        System.out.println("Copied List:   " + copiedList);
        // Modify the original list
        originalList.get(0).append(" Pie"); // Modifies the StringBuilder object
        System.out.println("\nAfter modifying original list's first element:");
        System.out.println("Original List: " + originalList); // Changed
        System.out.println("Copied List:   " + copiedList);   // Also changed!
    }
}

Output:

Original List: [Apple, Banana, Cherry]
Copied List:   [Apple, Banana, Cherry]
After modifying original list's first element:
Original List: [Apple Pie, Banana, Cherry]
Copied List:   [Apple Pie, Banana, Cherry]

Key Points:

  • Type: This is a shallow copy.
  • What it does: It creates a new list object, but the elements inside the new list are the same references to the objects in the original list.
  • Implication: If the elements are mutable (like StringBuilder, ArrayList, custom objects), changes made to an element through the original list will be reflected in the copied list, and vice-versa. If the elements are immutable (like String, Integer, LocalDate), this isn't a problem because you can't change the object itself, only replace it.
  • Modifiability: The new list is a completely separate list object, so you can add or remove elements from it without affecting the original list.

Using addAll() Method (Shallow Copy)

This is functionally identical to the copy constructor. You create a new, empty list and then add all elements from the original list to it.

List<String> originalList = Arrays.asList("A", "B", "C");
List<String> copiedList = new ArrayList<>(); // New list
copiedList.addAll(originalList); // Add all elements from original

Key Points:

  • Type: Shallow copy.
  • Use Case: Useful when you already have a list instance and want to populate it.

Using Java 8 Streams (Shallow Copy)

You can use the Stream API to create a new list. This is a modern and flexible approach.

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class StreamCopyExample {
    public static void main(String[] args) {
        List<String> originalList = Arrays.asList("X", "Y", "Z");
        // Create a shallow copy using a stream
        List<String> copiedList = originalList.stream()
                                             .collect(Collectors.toList());
        System.out.println("Original List: " + originalList);
        System.out.println("Copied List:   " + copiedList);
    }
}

Key Points:

  • Type: Shallow copy.
  • Flexibility: This is very powerful. You can easily transform the elements during the copy.
    // Example: Copy and transform to uppercase
    List<String> upperCaseList = originalList.stream()
                                             .map(String::toUpperCase)
                                             .collect(Collectors.toList());

Creating an Unmodifiable Copy (Immutable View)

Sometimes you don't want a copy that can be modified at all. You want a "read-only" view of the original list. The Collections.unmodifiableList() method is perfect for this.

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class UnmodifiableCopyExample {
    public static void main(String[] args) {
        List<String> originalList = new ArrayList<>(Arrays.asList("One", "Two", "Three"));
        // Create an unmodifiable view of the list
        List<String> unmodifiableList = Collections.unmodifiableList(originalList);
        System.out.println("Unmodifiable List: " + unmodifiableList);
        try {
            // This will throw an UnsupportedOperationException
            unmodifiableList.add("Four");
        } catch (UnsupportedOperationException e) {
            System.out.println("\nCannot modify the unmodifiable list!");
        }
        // Note: You can still modify the original list, and the unmodifiable view will reflect those changes.
        originalList.add("Four");
        System.out.println("Original List after modification: " + originalList);
        System.out.println("Unmodifiable List after original modification: " + unmodifiableList);
    }
}

Key Points:

  • Type: This is not a copy in the traditional sense. It's a view or a wrapper.
  • What it does: It returns a read-only wrapper around the original list.
  • Implication: You cannot add, remove, or change elements in the unmodifiableList. Any attempt to do so will throw an UnsupportedOperationException.
  • Key Difference: If you modify the originalList, the unmodifiableList will see those changes because it's just a view. This is different from a true copy.

Deep Copy (The "Real" Copy)

A deep copy creates a new list and new copies of all the objects within it. This is the only way to ensure that the copied list is completely independent of the original.

Java does not have a built-in, general-purpose deep copy method. You have to implement it yourself.

Example: Deep Copying a List of Custom Objects

Let's say you have a Person class.

// Person.java
public class Person {
    private String name;
    private int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String getName() { return name; }
    public int getAge() { return age; }
    public void setName(String name) { this.name = name; }
    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }
}

Now, let's create a deep copy utility.

import java.util.ArrayList;
import java.util.List;
public class DeepCopyExample {
    public static void main(String[] args) {
        List<Person> originalList = new ArrayList<>();
        originalList.add(new Person("Alice", 30));
        originalList.add(new Person("Bob", 25));
        // Create a deep copy
        List<Person> deepCopiedList = deepCopyList(originalList);
        System.out.println("Original List: " + originalList);
        System.out.println("Deep Copied List: " + deepCopiedList);
        // Modify an object in the original list
        originalList.get(0).setName("Alice Smith");
        System.out.println("\nAfter modifying original list's first element:");
        System.out.println("Original List: " + originalList); // Changed
        System.out.println("Deep Copied List: " + deepCopiedList); // Unchanged!
    }
    /**
     * Creates a deep copy of a list of objects that have a copy constructor.
     * @param originalList The list to copy.
     * @return A new list containing deep copies of the elements.
     */
    public static <T extends DeepCopyable> List<T> deepCopyList(List<T> originalList) {
        List<T> copy = new ArrayList<>(originalList.size());
        for (T item : originalList) {
            // Assumes T has a copy constructor: new T(item)
            copy.add(item.copy());
        }
        return copy;
    }
}
// Marker interface and implementation for our Person class
interface DeepCopyable {
    Object copy();
}
class Person implements DeepCopyable {
    // ... (same as above)
    @Override
    public Person copy() {
        // Create a new Person object with the same data
        return new Person(this.name, this.age);
    }
}

Output:

Original List: [Person{name='Alice', age=30}, Person{name='Bob', age=25}]
Deep Copied List: [Person{name='Alice', age=30}, Person{name='Bob', age=25}]
After modifying original list's first element:
Original List: [Person{name='Alice Smith', age=30}, Person{name='Bob', age=25}]
Deep Copied List: [Person{name='Alice', age=30}, Person{name='Bob', age=25}]

Key Points:

  • Type: Deep copy.
  • How it works: You iterate through the original list and create a new instance of each element. This requires that your element classes have a way to be copied (e.g., a copy constructor, a clone() method, or a serialization-based approach).
  • Complexity: Deep copying can be complex, especially if your objects contain other objects that also need to be deep copied.

Summary: Which Method Should I Use?

Method Type When to Use
new ArrayList<>(original) Shallow Copy The most common case. Use when elements are immutable (String, Integer) or when you don't mind shared references to mutable objects.
list.addAll(original) Shallow Copy Same as above. A good alternative if you already have a list instance.
stream().collect() Shallow Copy The modern, flexible approach. Best when you also need to transform the elements during the copy.
Collections.unmodifiableList() View (Not a Copy) When you need to guarantee that a list cannot be modified, but it can still see changes in the original list.
Custom Deep Copy Deep Copy When you need a completely independent copy and the elements are mutable objects. You must implement the copying logic yourself.
分享:
扫描分享到社交APP
上一篇
下一篇