1. 概述

本文将深入探讨 Java 中 HashMap 的浅拷贝与深拷贝概念,并提供多种实用的 HashMap 拷贝方法。

同时也会介绍一些第三方库,它们在某些场景下能帮助我们更高效地完成拷贝操作。

2. 浅拷贝 vs 深拷贝

在开始具体实现前,先明确 HashMap 浅拷贝与深拷贝的本质区别。

2.1 浅拷贝

浅拷贝指的是创建一个新的 HashMap,但其中的 key 和 value 都是指向原 HashMap 中对象的引用。

举个例子,我们定义一个 Employee 类,并将其作为 value 存入 HashMap:

public class Employee {
    private String name;

    // 构造方法、getter 和 setter 省略
}

初始化 HashMap:

HashMap<String, Employee> map = new HashMap<>();
Employee emp1 = new Employee("John");
Employee emp2 = new Employee("Norman");
map.put("emp1", emp1);
map.put("emp2", emp2);

浅拷贝后,验证是否是不同对象:

HashMap<String, Employee> shallowCopy = new HashMap<>(map); // 示例浅拷贝方式
assertThat(shallowCopy).isNotSameAs(map);

⚠️ 但因为是浅拷贝,如果我们修改了原始对象的属性,拷贝后的 map 也会受到影响:

emp1.setName("Johny");
assertThat(shallowCopy.get("emp1")).isEqualTo(map.get("emp1"));

2.2 深拷贝

深拷贝会创建一个全新的 HashMap,并且对每个 key 和 value 都进行深度复制,即创建新对象。

这样,即使修改原始对象,深拷贝后的 map 也不会受影响:

HashMap<String, Employee> deepCopy = deepCopyImplementation(map); // 示例深拷贝方式
emp1.setName("Johny");
assertThat(deepCopy.get("emp1")).isNotEqualTo(map.get("emp1"));

3. 使用 HashMap 原生 API 拷贝

3.1 使用 HashMap 构造函数

HashMap 提供了一个带 Map 参数的构造函数,可以快速完成浅拷贝:

HashMap<String, Employee> shallowCopy = new HashMap<>(originalMap);

3.2 使用 clone() 方法

HashMap 的 clone 方法也返回一个浅拷贝:

HashMap<String, Employee> shallowCopy = originalMap.clone();

3.3 使用 put() 方法逐个添加

通过遍历 entrySet 并逐个 put 到新 map 中:

HashMap<String, Employee> shallowCopy = new HashMap<>();
for (Map.Entry<String, Employee> entry : originalMap.entrySet()) {
    shallowCopy.put(entry.getKey(), entry.getValue());
}

3.4 使用 putAll() 方法

更简洁的方式是使用 putAll(),一次性拷贝所有映射:

HashMap<String, Employee> shallowCopy = new HashMap<>();
shallowCopy.putAll(originalMap);

⚠️ 注意:put()putAll() 都会覆盖已有 key 的值。

另外,HashMap 构造函数、clone()、putAll() 内部都调用了同一个方法 putMapEntries()

4. 使用 Java 8 Stream API 拷贝

✅ 可以使用 Java 8 的 Stream API 来完成 HashMap 的浅拷贝:

HashMap<String, Employee> shallowCopy = originalMap.entrySet().stream()
    .collect(Collectors.toMap(
        Map.Entry::getKey,
        Map.Entry::getValue
    ));

⚠️ 注意:Collectors.toMap 返回的是 Map 类型,需要强制转换为 HashMap

5. 使用 Google Guava

Google Guava 提供了丰富的 Map 工具类,可以方便地创建不可变的浅拷贝:

Map<String, Employee> originalMap = ImmutableMap.<String, Employee>builder()
    .put("emp1", emp1)
    .put("emp2", emp2)
    .build();

Map<String, Employee> shallowCopy = ImmutableMap.copyOf(originalMap);

assertThat(shallowCopy).isSameAs(originalMap); // 不可变 map 的 copyOf 可能直接返回原对象

6. 使用 Apache Commons Lang 深拷贝

❌ Java 原生没有提供深拷贝实现。

✅ Apache Commons Lang 提供了 SerializationUtils.clone() 方法,可用于深拷贝,前提是类实现了 Serializable 接口:

public class Employee implements Serializable {
    private String name;

    // 构造方法、getter 和 setter 省略
}

进行深拷贝:

HashMap<String, Employee> deepCopy = SerializationUtils.clone(originalMap);

⚠️ 注意:该方法依赖对象序列化,性能较低,适合结构复杂、需要真正深拷贝的场景。

7. 总结

方法 类型 是否支持深拷贝 备注
构造函数 浅拷贝 简单高效
clone() 浅拷贝 简洁
putAll() 浅拷贝 推荐方式
Stream API 浅拷贝 函数式风格
Guava 浅拷贝 适合不可变 map
Apache Commons Lang 深拷贝 需实现 Serializable

📌 根据实际需求选择合适的拷贝方式,避免不必要的性能开销。对于深拷贝,建议根据业务场景选择序列化、手动 clone 或第三方库。


完整的示例代码及单元测试可在 GitHub 仓库 中查看。


原始标题:Copying a HashMap in Java