下面我将详细介绍几种最常用和最重要的方法,并附上代码示例和优缺点分析。

核心概念:浅拷贝 vs. 深拷贝
在开始之前,必须理解这两个概念:
- 浅拷贝:创建一个新列表,但新列表中的元素引用与原列表中的元素引用是相同的,如果元素是基本数据类型(如
int,double),则复制其值,如果元素是对象,则复制的是对象的引用(地址),而不是对象本身,修改新列表中的对象会影响原列表中的对应对象。 - 深拷贝:创建一个新列表,并且递归地复制所有元素,为新对象创建新的实例,新列表和原列表中的元素是完全独立的,修改一个不会影响另一个。
使用构造函数(最推荐,用于浅拷贝)
这是最直接、最简洁、性能也最好的方式来创建一个 ArrayList 的浅拷贝。
语法
ArrayList<YourType> newList = new ArrayList<>(originalList);
代码示例
import java.util.ArrayList;
import java.util.Arrays;
public class ArrayListCopyExample {
public static void main(String[] args) {
// 1. 原始列表
ArrayList<String> originalList = new ArrayList<>(Arrays.asList("Apple", "Banana", "Cherry"));
System.out.println("原始列表: " + originalList);
// 2. 使用构造函数进行浅拷贝
ArrayList<String> shallowCopyList = new ArrayList<>(originalList);
System.out.println("浅拷贝列表: " + shallowCopyList);
// 3. 修改原始列表
originalList.add("Date");
System.out.println("\n修改原始列表后 (添加元素):");
System.out.println("原始列表: " + originalList);
System.out.println("浅拷贝列表: " + shallowCopyList); // 拷贝列表不受影响
// 4. 修改列表中的对象(如果元素是可变的)
// 注意:String 是不可变的,我们用一个可变的对象来演示
class Person {
String name;
Person(String name) { this.name = name; }
@Override
public String toString() { return name; }
}
ArrayList<Person> personList = new ArrayList<>(Arrays.asList(new Person("Alice"), new Person("Bob")));
ArrayList<Person> personCopyList = new ArrayList<>(personList);
System.out.println("\n--- 修改对象引用 ---");
System.out.println("修改前 personList: " + personList);
System.out.println("修改前 personCopyList: " + personCopyList);
// 修改 personList 中的第一个 Person 对象
personList.get(0).name = "Anna";
System.out.println("\n修改 personList[0] 后:");
System.out.println("personList: " + personList); // Anna, Bob
System.out.println("personCopyList: " + personCopyList); // Anna, Bob (也被修改了!)
}
}
优点
- 代码简洁:一行代码即可完成。
- 性能高:这是 JVM 优化的标准方式,通常比手动循环复制更快。
- 线程安全(相对于
clone()):避免了clone()方法可能带来的各种问题。
缺点
- 仅限浅拷贝:对于包含可变对象的列表,拷贝和原列表会共享这些对象。
使用 clone() 方法(不推荐)
ArrayList 实现了 Cloneable 接口,理论上可以使用 clone() 方法。

代码示例
import java.util.ArrayList;
import java.util.Arrays;
public class ArrayListCloneExample {
public static void main(String[] args) {
ArrayList<String> originalList = new ArrayList<>(Arrays.asList("A", "B", "C"));
ArrayList<String> clonedList = (ArrayList<String>) originalList.clone(); // 需要强制类型转换
System.out.println("原始列表: " + originalList);
System.out.println("克隆列表: " + clonedList);
}
}
为什么不推荐?
- 返回类型是
Object:需要强制类型转换,很麻烦且不安全。 - 是浅拷贝:和方法一一样,是浅拷贝。
- 性能可能不佳:
clone()方法的内部实现有时不如构造函数高效。 - 与
Cloneable接口的设计缺陷有关:整个clone()机制在 Java 中就存在争议,被认为是“有缺陷的模式”。
除非你有非常特殊的需求,否则永远不要使用 clone() 来复制 ArrayList,请优先使用构造函数。
手动循环(适用于深拷贝或自定义逻辑)
当你需要执行深拷贝或者对复制的每个元素进行额外处理时,手动循环是最佳选择。
代码示例(深拷贝)
假设我们有一个 Person 类,我们想创建一个包含新 Person 对象的列表。
import java.util.ArrayList;
import java.util.Arrays;
class Person {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return name + "(" + age + ")";
}
}
public class DeepCopyExample {
public static void main(String[] args) {
ArrayList<Person> originalList = new ArrayList<>(Arrays.asList(
new Person("Alice", 30),
new Person("Bob", 25)
));
// 手动创建深拷贝
ArrayList<Person> deepCopyList = new ArrayList<>(originalList.size()); // 预分配空间
for (Person person : originalList) {
// 为每个对象创建一个新实例
deepCopyList.add(new Person(person.name, person.age));
}
System.out.println("修改前 originalList: " + originalList);
System.out.println("修改前 deepCopyList: " + deepCopyList);
// 修改原始列表中的对象
originalList.get(0).name = "Anna";
originalList.get(0).age = 31;
System.out.println("\n修改 originalList[0] 后:");
System.out.println("originalList: " + originalList); // Anna(31), Bob(25)
System.out.println("deepCopyList: " + deepCopyList); // Alice(30), Bob(25) (未受影响)
}
}
优点
- 灵活性最高:可以执行深拷贝,也可以在复制时过滤、转换或修改元素。
- 逻辑清晰:对于复杂的复制逻辑,代码更易读和维护。
缺点
- 代码冗长:需要编写更多的样板代码。
- 性能可能稍差:对于简单的浅拷贝,比构造函数慢。
使用 Java 8 Stream API(现代且灵活)
Java 8 引入的 Stream API 提供了一种非常优雅和函数式的方式来处理集合操作,包括复制。

代码示例(浅拷贝和深拷贝)
import java.util.ArrayList;
import java.util.Arrays;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class StreamCopyExample {
public static void main(String[] args) {
// 1. 浅拷贝
ArrayList<String> originalList = new ArrayList<>(Arrays.asList("A", "B", "C"));
ArrayList<String> shallowCopyStream = originalList.stream()
.collect(Collectors.toCollection(ArrayList::new));
System.out.println("浅拷贝 (Stream): " + shallowCopyStream);
// 2. 深拷贝 (结合方法引用)
class Item {
int id;
Item(int id) { this.id = id; }
@Override public String toString() { return "Item-" + id; }
}
ArrayList<Item> originalItemList = new ArrayList<>(Arrays.asList(new Item(1), new Item(2)));
// 使用 map 创建新对象,然后收集
ArrayList<Item> deepCopyStream = originalItemList.stream()
.map(item -> new Item(item.id)) // 创建新 Item 对象
.collect(Collectors.toCollection(ArrayList::new));
System.out.println("\n修改前 originalItemList: " + originalItemList);
System.out.println("修改前 deepCopyStream: " + deepCopyStream);
originalItemList.get(0).id = 99;
System.out.println("\n修改 originalItemList[0] 后:");
System.out.println("originalItemList: " + originalItemList); // Item-99, Item-2
System.out.println("deepCopyStream: " + deepCopyStream); // Item-1, Item-2 (未受影响)
}
}
优点
- 代码简洁且现代:一行代码即可完成,非常适合函数式编程风格。
- 灵活性高:可以轻松地在
map()等中间操作中插入自定义逻辑,实现深拷贝或元素转换。 - 可读性强:对于熟悉 Stream 的开发者来说,意图非常明确。
缺点
- 性能开销:对于非常小的列表,Stream 的性能可能不如简单的 for 循环或构造函数,但对于大多数应用场景,这点性能差异可以忽略不计。
总结与最佳实践
| 方法 | 语法 | 类型 | 推荐度 | 备注 |
|---|---|---|---|---|
| 构造函数 | new ArrayList<>(original) |
浅拷贝 | ⭐⭐⭐⭐⭐ | 首选,简洁、高效、安全。 |
clone() |
original.clone() |
浅拷贝 | ⭐ | 不推荐,有设计缺陷,代码不优雅。 |
| 手动循环 | for (...) { ... } |
深拷贝 | ⭐⭐⭐⭐ | 需要深拷贝或自定义逻辑时的最佳选择。 |
| Stream API | stream().collect(...) |
浅拷贝/深拷贝 | ⭐⭐⭐⭐⭐ | 现代、灵活、优雅,是构造函数和手动循环的强力补充。 |
如何选择?
-
只需要一个简单的、独立的列表副本(浅拷贝):
- 直接使用构造函数:
new ArrayList<>(originalList);
- 直接使用构造函数:
-
列表中的元素是基本类型或不可变对象(如
String):- 使用构造函数或 Stream API,因为元素本身不会被修改,所以浅拷贝已经足够。
-
列表中的元素是可变对象,并且你需要两个完全独立的列表(深拷贝):
- 使用手动循环:最可控,逻辑最清晰。
- 使用 Stream API:如果团队熟悉函数式编程,这是更优雅的选择。
-
在复制过程中需要对元素进行过滤或转换:
- 使用 Stream API:这是它的强项,只复制符合条件的元素,或者将元素类型从
A转换为B。
- 使用 Stream API:这是它的强项,只复制符合条件的元素,或者将元素类型从
new ArrayList<>(original) 应该是你 90% 情况下的默认选择,当需要更复杂的行为时,再考虑 手动循环 或 Stream API。
