hashmap的clone()方法实现浅拷贝,仅复制哈希表结构和键值对引用,不递归克隆key/value对象本身,因此原map与克隆map共享可变值对象,修改其状态会相互影响。

Java 中 HashMap 的 clone() 方法实现的是**浅拷贝**,它只复制了哈希表的结构(如数组、节点引用、容量、阈值等),但**不递归复制键值对对象本身**。也就是说,新旧 HashMap 共享同一组 key 和 value 对象的引用。
clone 方法的核心逻辑
在 JDK 8 的 HashMap 源码中,clone() 方法定义如下:
public Object clone() {
HashMap<k> result;
try {
result = (HashMap<k>)super.clone();
} catch (CloneNotSupportedException e) {
throw new InternalError(e);
}
result.table = new Node[capacity];
result.entrySet = null;
result.keySet = null;
result.values = null;
result.modCount = 0;
result.size = 0;
result.threshold = initialCapacity;
// 关键:重新 put 所有 entry,但只复制节点引用,不 clone key/value
for (Node<k> e : table) {
while (e != null) {
result.put(e.key, e.value);
e = e.next;
}
}
return result;
}</k></k></k>
注意:上面是简化逻辑,实际 JDK 8 中 clone() 并未直接遍历 table 再 put,而是更高效地做了以下几件事:
- 调用
super.clone()复制对象字段(包括table数组引用) - 但立即为新实例创建一个空的新 table 数组(长度同原 capacity)
- 重置缓存视图(
entrySet、keySet、values)为null - 将原 map 中所有键值对通过
put逐个插入新 map —— 这步会触发哈希计算和链表/红黑树重建,但 key 和 value 的引用直接复用
为什么是浅拷贝?关键看引用关系
假设你有:
Map<string stringbuilder> map1 = new HashMap();
StringBuilder sb = new StringBuilder("hello");
map1.put("k1", sb);</string>
执行 Map<string stringbuilder> map2 = (HashMap)map1.clone();</string> 后:
-
map1和map2是两个独立的HashMap实例(修改 size、扩容、增删 entry 互不影响) - 但
map1.get("k1") == map2.get("k1")返回 true(指向同一个StringBuilder对象) - 若调用
map2.get("k1").append("!"),map1.get("k1")的内容也会改变
如何实现深拷贝?
HashMap 本身不提供深拷贝,需手动处理。常见方式有:
-
键值对象自身支持 clone(如实现了
Cloneable):map2.put(k.clone(), v.clone());(需确保类型安全且非 null) -
使用序列化反序列化(适用于可序列化对象):
通过ObjectOutputStream+ByteArrayInputStream实现“伪深拷贝” -
借助工具类(如 Apache Commons Lang 的
SerializationUtils.clone()) -
构造新 map 时显式转换:
new HashMap(map1.entrySet().stream().collect(Collectors.toMap(...))),并在映射中新建 key/value 实例
注意事项
由于 clone() 是浅拷贝,使用时需特别注意:
- 如果 key 或 value 是可变对象(如
ArrayList、StringBuilder、自定义 POJO),修改其状态会影响两个 map - 若 key 依赖其内部状态(如修改了影响
hashCode()的字段),可能导致原 map 查找正常,而克隆后 map 出现get()失败 -
clone()不是线程安全操作;并发修改原 map 可能导致克隆结果不一致
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











