1. 概述
在 Java 开发中,Set
是一种不包含重复元素的集合结构。Set
是一个接口,继承自 Collection
接口。
本文将介绍几种在 Java 中复制 Set
的常见方法。每种方式都有其适用场景和注意事项,适用于不同需求的开发场景。
2. 使用拷贝构造函数
一种常见的做法是使用具体实现类(如 HashSet
)的构造函数来复制 Set:
Set<T> copy = new HashSet<>(original);
✅ 这种方式简单直接,但需要注意:
- 它只是浅拷贝,复制的是对象引用而非对象本身。
- 如果原始集合中的对象被修改,复制后的集合也会受到影响。
3. 使用 addAll 方法
Set
接口提供了 addAll
方法,可以将一个集合中的所有元素添加到另一个集合中:
Set<T> copy = new HashSet<>();
copy.addAll(original);
✅ 这种方式适用于需要手动控制目标集合类型的情况。
⚠️ 同样只进行浅拷贝,不适用于需要深拷贝的场景。
4. 使用 clone 方法
Java 中的 Set
实现类(如 HashSet
、TreeSet
等)都实现了 Cloneable
接口,并重写了 clone()
方法:
Set<T> copy = (Set<T>) original.clone();
✅ 快速、简洁,适合浅拷贝场景。
⚠️ 注意以下几点:
- 返回的是
Object
类型,需要强制转换为Set<T>
。 - 默认实现是浅拷贝,如需深拷贝需要自定义实现。
5. 使用 JSON 序列化/反序列化
通过将 Set 序列化为 JSON 字符串再反序列化回来,可以实现深拷贝:
Gson gson = new Gson();
String jsonStr = gson.toJson(original);
Set<T> copy = gson.fromJson(jsonStr, Set.class);
✅ 适用于需要深拷贝的场景。
❌ 要求集合元素必须实现 Serializable
接口,且性能相对较低。
6. 使用 Apache Commons Lang
Apache Commons Lang 提供了 SerializationUtils.clone()
方法,可以用于克隆对象:
Set<T> copy = new HashSet<>();
for (T item : original) {
copy.add(SerializationUtils.clone(item));
}
✅ 适用于需要深拷贝的场景。
❌ 同样要求元素必须实现 Serializable
接口,且需要遍历集合逐个克隆。
7. 使用 Java 8 的 Stream API
Java 8 引入了 Stream API,配合 Collectors.toSet()
可以简洁地复制 Set:
Set<T> copy = original.stream()
.collect(Collectors.toSet());
✅ 简洁、函数式风格,适合中间需要处理元素的场景(如过滤、跳过等)。
⚠️ 依然是浅拷贝。
8. 使用 Java 10 的 Set.copyOf
Java 10 在 Set
接口中新增了一个静态方法 copyOf
,用于创建一个不可变的 Set 副本:
Set<T> copy = Set.copyOf(original);
✅ 简单高效,适用于只读场景。
❌ 返回的 Set 是不可变的,不能添加或删除元素。
❌ 参数不能为 null,否则抛出异常。
9. 总结
方法 | 是否浅拷贝 | 是否支持深拷贝 | 是否可变 | 备注 |
---|---|---|---|---|
构造函数 | ✅ | ❌ | ✅ | 简单粗暴,适合快速复制 |
addAll | ✅ | ❌ | ✅ | 控制目标集合类型 |
clone | ✅ | ❌ | ✅ | 注意强制类型转换 |
JSON | ❌ | ✅ | ✅ | 性能略低,需序列化支持 |
Apache Commons Lang | ❌ | ✅ | ✅ | 需遍历元素 |
Stream API | ✅ | ❌ | ✅ | 函数式编程风格 |
Set.copyOf (Java 10+) | ✅ | ❌ | ❌ | 不可变副本,适合只读 |
根据实际需求选择合适的方式,避免不必要的性能开销或功能缺失。例如:
- 只需浅拷贝 ➡️ 用构造函数或
addAll
- 需要深拷贝 ➡️ 用 JSON 或 Apache Commons Lang
- 只读副本 ➡️ Java 10 的
Set.copyOf
如需查看完整示例代码,请参考:GitHub 示例仓库(含 Java 10 示例)