文章目录
1 先看两段代码
2.1当value为基本数据类型或字符串时
@Test
public void MapDemo1() {
Map<String, String> map = new HashMap<>();
System.err.println("map加入内容前=" + System.identityHashCode(map));
map.put("热火", "wade");
System.err.println("map加入内容后=" + System.identityHashCode(map));
Map<String, String> copyMap1 = new HashMap<>();
System.err.println("copyMap2加入内容前=" + System.identityHashCode(copyMap1));
copyMap1 = map;
System.err.println("copyMap2加入内容后=" + System.identityHashCode(copyMap1));
Map<String, String> copyMap2 = new HashMap<>();
System.err.println("copyMap1加入内容前=" + System.identityHashCode(copyMap2));
copyMap2.putAll(map);
System.err.println("copyMap1加入内容后=" + System.identityHashCode(copyMap2));
map.put("热火", "james");
System.out.println("直接修改的====" + System.identityHashCode(map) + "===" + map);
System.out.println("不想修改的====" + System.identityHashCode(copyMap1) + copyMap1);
System.out.println("不想修改的====" + System.identityHashCode(copyMap2) + copyMap2);
}
代码运行结果和结论如下:
2.2 当value为非字符串非基本数据类型的包装类型的引用数据类型时
@Test
public void MapDemo2() {
Company company1 = new Company("heat", "wade");
Map<String, Company> map = new HashMap<>();
System.err.println("map加入内容前=" + System.identityHashCode(map));
map.put("热火", company1);
System.err.println("map加入内容后=" + System.identityHashCode(map));
Map<String, Company> copyMap1 = new HashMap<>();
System.out.println("copyMap1加入内容前=" + System.identityHashCode(copyMap1));
copyMap1 = map;
System.out.println("copyMap1加入内容后=" + System.identityHashCode(copyMap1));
Map<String, Company> copyMap2 = new HashMap<>();
System.err.println("copyMap2加入内容前=" + System.identityHashCode(copyMap2));
copyMap2.putAll(map);
System.err.println("copyMap2加入内容后=" + System.identityHashCode(copyMap2));
company1.setCompanyCode("1001");
System.out.println("直接修改的====" + System.identityHashCode(map) + "===" + map);
System.out.println("不想修改的====" + System.identityHashCode(copyMap1) + "===" + copyMap1);
System.out.println("不想修改的====" + System.identityHashCode(copyMap2) + "===" + copyMap2);
}
代码运行结果和结论如下:
通过上面两段代码可以看到:
(1)当value为基本数据类型或字符串时 ------> Map的putAll()方法进行了深拷贝
(2)当value为非字符串的引用数据类型时 —> Map的putAll()方法并没有进行深拷贝
2 从JVM内存模型的角度来深入的聊一聊原因
2.1 当value为基本数据类型或字符串时
其基本过程如下:
2.2 当value为非字符串的引用数据类型时
其基本过程如下:
小结+深入探讨出现浅拷贝的原因
首先应该知道Java有8中基本数据类型(byte、short、int、long、float、double、char、boolean),其它的统称为引用数据类型。
其次在Java语言里万事万物皆对象。
再其次Java里的对象肯定都是由基本数据类型+引用数据类型的变量组成的,但是由于节省内存等方面的考虑,对象里真正存放的数据为基本类型的变量对应的数据、基本类型的包装类型的变量对应的数据和String类型的变量对应的数据(当然对于String类型严格意义上来讲应该并不是这样,只是我们可以这样认为);其他引用数据类型的变量 并不会存放数据本身,而是存放该引用数据类型变量的地址,如下图所示:
由此便可以知道,开发中之所以出现浅拷贝,是因为你拷贝的对象中有对象、数组、集合等引用数据类型。
3 如何进行深拷贝
3.1 真实开发中可能碰到的浅拷贝的栗子即解决方案
在实际开发中,for循环的层数肯定不能过多,因为数据量一上来,很容器造成接口响应时间过长的问题,为了达到这个目的,我往往的做法是将某一层的for循环先转成Map,以此来压缩for循环的层数,这时候非常容易出现的一个浅拷贝的bug就是在其他for循环里修改预先转成的Map的值
,然后将修改后的值作为for循环里的返回值。
针对上面我说的这种情况,其实有两种解决方法,
(1)第一种就是死活不能改预先转成的Map,而是在for循环里将每次循环从Map里拿出的对象取出,然后新建一个对象,将从Map里拿出的对象的属性全部复制给新建的对象,然后拿着新建的对象进行操作
(2)第二种也是死活不能改预先转成的Map,而是在for循环里,首先将预先取出的Map对象,进行深拷贝一份,然后拿着该深拷贝的Map进行本次for循环里的操作,这样每次for循环都是操作的深拷贝的Map,每次循环修改的对象,肯定也不会是一个对象 —》 当然这种方式应该比(1)更耗性能。
3.2 本文Map< String, Company >属性的深拷贝
其实到这里我想人人应该都能解决这个问题了,我这里给个栗子,代码如下:
@Test
public void MapDemo2_1() {
Company company1 = new Company("heat", "wade");
Map<String, Company> map = new HashMap<>();
System.out.println("map加入内容前=" + System.identityHashCode(map));
map.put("热火", company1);
System.out.println("map加入内容后=" + System.identityHashCode(map));
Map<String, Company> copyMap2 = new HashMap<>();
System.err.println("copyMap2加入内容前=" + System.identityHashCode(copyMap2));
//(1)从map中取出company1
Company company1_1 = map.get("热火");
//(2)新建一个Company对象 --- JVM会在堆中为其分配一个新的地址
Company deepCopyCompany = new Company();
//(3)将从map中取出的Company对像的属性复制给新的Company对象
BeanUtils.copyProperties(company1_1, deepCopyCompany);
//(4)将新的对象给copyMap2 ---> 完成深拷贝的过程
copyMap2.put("热火", deepCopyCompany);
System.err.println("copyMap2加入内容后=" + System.identityHashCode(copyMap2));
//修改company1--->可以发现map中Company对象的值会变化,但copyMap2的不会
company1.setCompanyCode("1001");
System.out.println("直接修改的====" + System.identityHashCode(map) + map);
System.out.println("不想修改的====" + System.identityHashCode(copyMap2) + copyMap2);
}
测试结果如下: