HashMap.clone()返回浅拷贝但原值修改不同步的原因及警告解决
对浅拷贝的理解偏差说明
HashMap.clone() 确实返回浅拷贝结果,认知偏差的核心是混淆了Map实例本身的存储结构和Map里存储的键值对象引用两个概念:
- 浅拷贝的「浅」,指的是拷贝动作只会复制键值对的引用,不会额外生成新的键、值对象实例:也就是说克隆得到的是一个全新的、独立的HashMap实例,它内部存的键引用、值引用和原Map里存的引用指向同一个内存对象,但两个Map各自维护自己的条目存储结构,互不干扰。
- 测试代码里调用
map.replace(3, "google"),本质是修改原Map自身的存储结构:把原Map里键3对应的value引用,从指向"yahoo"字符串改成指向"google"字符串,这个操作完全没有触碰"yahoo"这个值对象本身。克隆出来的mClone自己的存储结构里,键3对应的value引用还是指向原来的"yahoo",自然不会同步变更。 - 如果写
Map mClone = map这种直接赋值的代码,两个变量才会指向同一个HashMap实例,这时候对原变量做replace操作,mClone才会同步看到变化——但这根本不是拷贝,只是给同一个对象起了个别名。
真正能体现浅拷贝特性的场景,是不修改Map的条目结构,直接修改Map里存储的可变值对象的内部状态,举个例子:
// 自定义可变值类型 class MutableItem { public String content; public MutableItem(String content) {this.content = content;} } public class TestShallowCopy { public static void main(String[] args) { HashMap<Integer, MutableItem> map = new HashMap<>(); map.put(3, new MutableItem("yahoo")); Map<Integer, MutableItem> mClone = (HashMap<Integer, MutableItem>) map.clone(); // 不替换引用,直接修改值对象本身的属性 map.get(3).content = "google"; // 浅拷贝下两个Map存的值引用指向同一个对象,所以克隆Map也会读到修改后的值 System.out.println(mClone.get(3).content); // 输出google } }
原测试用String作为值类型,而String是不可变类,根本无法在不替换引用的前提下修改对象本身的内容,自然测不出浅拷贝的共享特性。
Unchecked cast 警告修复方案
这个警告的来源是HashMap.clone()的返回值类型是类型擦除后的Object,编译器无法在编译期确认强转的HashMap<Integer, String>泛型类型是安全的,常见修复方案有两种:
- 优先推荐:不用
clone()方法,改用HashMap自带的拷贝构造器生成新Map,完全规避泛型强转,效果和clone()一致都是浅拷贝,也是工业界最常用的写法:Map<Integer, String> mClone = new HashMap<>(map); - 备选方案:如果一定要用
clone()方法,可以用@SuppressWarnings("unchecked")注解压制警告,注意要把注解的作用范围限制到最小,不要直接标注在类上:
这种方案只是告知编译器跳过类型检查,需要自行保证强转的类型安全性,运行期如果类型不匹配依然会抛出异常,因此不做优先推荐。@SuppressWarnings("unchecked") Map<Integer, String> mClone = (HashMap<Integer, String>) map.clone();
内容的提问来源于stack exchange,提问作者Troskyvs
相关产品推荐
相关产品推荐

