杰瑞科技汇

java arraylist 复制

构造函数方法(推荐用于浅拷贝)

这是最直接、最常用的方法,用于创建一个与原列表内容相同的新列表。

java arraylist 复制-图1
(图片来源网络,侵删)

语法

ArrayList<NewType> newList = new ArrayList<>(originalList);

示例

import java.util.ArrayList;
public class ArrayListCopyExample {
    public static void main(String[] args) {
        ArrayList<String> originalList = new ArrayList<>();
        originalList.add("Apple");
        originalList.add("Banana");
        originalList.add("Orange");
        // 使用构造函数创建一个新的 ArrayList
        ArrayList<String> copiedList = new ArrayList<>(originalList);
        System.out.println("Original List: " + originalList);
        System.out.println("Copied List: " + copiedList);
        // 修改原列表
        originalList.add("Grape");
        System.out.println("\nAfter adding 'Grape' to originalList:");
        System.out.println("Original List: " + originalList);
        System.out.println("Copied List: " + copiedList); // copiedList 不受影响
        // 修改 copiedList 中的元素
        copiedList.set(0, "Pineapple");
        System.out.println("\nAfter setting index 0 to 'Pineapple' in copiedList:");
        System.out.println("Original List: " + originalList); // originalList 不受影响
        System.out.println("Copied List: " + copiedList);
    }
}

输出

Original List: [Apple, Banana, Orange]
Copied List: [Apple, Banana, Orange]
After adding 'Grape' to originalList:
Original List: [Apple, Banana, Orange, Grape]
Copied List: [Apple, Banana, Orange]
After setting index 0 to 'Pineapple' in copiedList:
Original List: [Apple, Banana, Orange, Grape]
Copied List: [Pineapple, Banana, Orange]

特点

  • 浅拷贝:这是最关键的一点,新列表 copiedList 会复制原列表 originalList 中的所有引用,而不是对象本身。
  • 独立的列表容器copiedListoriginalList 是两个完全独立的 ArrayList 对象,你向其中一个添加或删除元素,不会影响另一个。
  • 共享元素:如果列表中的元素是可变对象(如 StringBuilder, 自定义类的实例),那么修改其中一个列表中的对象,会影响到另一个列表中对应的对象。
  • 性能:时间复杂度为 O(n),n 是原列表的大小,因为它需要遍历并复制所有引用。

clone() 方法

ArrayList 实现了 Cloneable 接口,所以可以使用 clone() 方法。

示例

import java.util.ArrayList;
public class ArrayListCloneExample {
    public static void main(String[] args) {
        ArrayList<String> originalList = new ArrayList<>();
        originalList.add("Apple");
        originalList.add("Banana");
        // 使用 clone() 方法
        ArrayList<String> copiedList = (ArrayList<String>) originalList.clone();
        System.out.println("Original List: " + originalList);
        System.out.println("Copied List: " + copiedList);
    }
}

特点

  • 浅拷贝:与构造函数方法完全一样,它也是进行浅拷贝。
  • 返回类型clone() 方法的返回类型是 Object,所以必须进行强制类型转换,这可能会带来一些麻烦(比如如果类型不匹配会抛出 ClassCastException)。
  • 不推荐:在 Effective Java 等权威著作中,通常不推荐使用 clone() 方法,因为它可能会被错误地实现,其行为有时不符合预期,使用构造函数是更清晰、更安全的选择。

Java 8+ Stream API

如果你需要对列表进行转换或过滤后再复制,Stream API 是一个非常强大的工具。

语法

ArrayList<NewType> newList = originalList.stream()
                                         .collect(Collectors.toCollection(ArrayList::new));

示例

import java.util.ArrayList;
import java.util.stream.Collectors;
public class ArrayListStreamExample {
    public static void main(String[] args) {
        ArrayList<Integer> originalList = new ArrayList<>();
        originalList.add(1);
        originalList.add(2);
        originalList.add(3);
        // 使用 Stream API 复制,并可以对元素进行转换
        ArrayList<String> copiedList = originalList.stream()
                .map(String::valueOf) // 将 Integer 转换为 String
                .collect(Collectors.toCollection(ArrayList::new));
        System.out.println("Original List: " + originalList);
        System.out.println("Copied List (as Strings): " + copiedList);
    }
}

特点

  • 浅拷贝:Stream API 的底层操作同样是复制引用。
  • 灵活性:最大的优点在于可以在复制的同时对元素进行处理,如 map, filter, sorted 等。
  • 性能:对于简单的复制,性能可能略逊于构造函数,因为它涉及流管道的开销,但对于复杂的转换操作,这是最优雅的方式。

addAll() 方法

这个方法不是直接创建一个新列表,而是将原列表的所有元素添加到一个已存在的列表中。

示例

import java.util.ArrayList;
public class ArrayListAddAllExample {
    public static void main(String[] args) {
        ArrayList<String> originalList = new ArrayList<>();
        originalList.add("Apple");
        originalList.add("Banana");
        // 创建一个空的新列表
        ArrayList<String> copiedList = new ArrayList<>();
        // 将原列表的所有元素添加到新列表中
        copiedList.addAll(originalList);
        System.out.println("Original List: " + originalList);
        System.out.println("Copied List: " + copiedList);
    }
}

特点

  • 浅拷贝:同样是浅拷贝。
  • 需要预先存在目标列表:你必须先创建一个新的 ArrayList 实例,然后才能调用 addAll
  • 适用场景:当你已经有一个列表,并希望将另一个列表的内容追加到它时,这个方法很合适,但如果只是为了复制,不如构造函数简洁。

深拷贝 vs. 浅拷贝

前面介绍的所有方法都是浅拷贝,理解浅拷贝和深拷贝的区别至关重要。

java arraylist 复制-图2
(图片来源网络,侵删)

浅拷贝

  • 行为:复制列表的容器(ArrayList 对象本身),但列表中的元素仍然是原列表元素的引用

  • 影响

    • 对列表本身的修改(如 add, remove)不会影响另一个列表。
    • 对列表中可变对象的修改会同时影响两个列表。
  • 示例

    class Person {
        String name;
        Person(String name) { this.name = name; }
        @Override public String toString() { return name; }
    }
    ArrayList<Person> list1 = new ArrayList<>();
    list1.add(new Person("Alice"));
    ArrayList<Person> list2 = new ArrayList<>(list1); // 浅拷贝
    list1.get(0).name = "Bob"; // 修改 list1 中的对象
    System.out.println(list1.get(0)); // 输出: Bob
    System.out.println(list2.get(0)); // 输出: Bob (也被修改了!)

深拷贝

  • 行为:不仅复制列表的容器,还复制列表中的每一个元素,创建全新的对象。

    java arraylist 复制-图3
    (图片来源网络,侵删)
  • 影响:两个列表完全独立,互不影响。

  • 如何实现:Java 没有内置的深拷贝方法,你需要手动实现。

    • 方法1:序列化与反序列化(通用但较慢)

      import java.io.*;
      public class DeepCopyUtil {
          @SuppressWarnings("unchecked")
          public static <T extends Serializable> ArrayList<T> deepCopy(ArrayList<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 (ArrayList<T>) ois.readObject();
              } catch (IOException | ClassNotFoundException e) {
                  throw new RuntimeException("Failed to deep copy the list", e);
              }
          }
      }

      注意:列表中的所有元素类都必须实现 Serializable 接口。

    • 方法2:手动创建并复制(推荐,更高效、更安全)

      // 假设 Person 类有一个拷贝构造函数
      // class Person { ... public Person(Person other) { this.name = other.name; } }
      ArrayList<Person> list1 = new ArrayList<>();
      list1.add(new Person("Alice"));
      ArrayList<Person> list2 = new ArrayList<>();
      for (Person p : list1) {
          list2.add(new Person(p)); // 使用拷贝构造函数创建新对象
      }
      list1.get(0).name = "Bob";
      System.out.println(list1.get(0)); // 输出: Bob
      System.out.println(list2.get(0)); // 输出: Alice (未被修改)

总结与最佳实践

方法 类型 优点 缺点 适用场景
构造函数 new ArrayList<>(original) 浅拷贝 最简洁、最安全、性能好 无法在复制时转换元素 日常浅拷贝的首选方法
clone() 浅拷贝 语法看起来简单 需要强制类型转换,不推荐使用 应尽量避免
Stream API 浅拷贝 灵活,可同时进行转换 代码稍长,简单拷贝性能略低 需要对元素进行转换或过滤时
addAll() 浅拷贝 直观 需要先创建目标列表 向已存在的列表追加内容时
手动深拷贝 深拷贝 完全独立,安全可控 实现复杂,需要元素类支持 当列表包含可变对象且需要完全隔离时

核心建议:

  1. 如果只是想复制一个 ArrayList 并且不关心元素是否被共享(即浅拷贝),请始终使用构造函数:

    ArrayList<String> copy = new ArrayList<>(original);
  2. 如果你的列表包含可变对象(如 Date, 自定义类实例),并且你需要两个列表完全独立,那么你必须实现深拷贝。 手动为每个元素实现拷贝逻辑(如拷贝构造函数或 clone() 方法)是最佳实践。

  3. 如果需要在复制的同时对元素进行转换,请使用 Stream API。

分享:
扫描分享到社交APP
上一篇
下一篇