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 实现类(如 HashSetTreeSet 等)都实现了 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 示例)


原始标题:Copying Sets in Java

« 上一篇: Java Weekly, 第285期
» 下一篇: SPF4J 入门指南