Map拷贝 关于对象深拷贝 浅拷贝的问题

1、概念

浅复制:则是只复制对象的引用,两个引用仍然指向同一个对象,在内存中占用同一块内存。被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。

深复制:被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍。

2、浅拷贝-复制对象引用

Map<String,String> map = new HashMap<>();
Map<String,String> map1 = map;
map.put("a","A");
System.out.println(map);
System.out.println(map1);

输出结果

{a=A}
{a=A}

分析

map1复制的是对象map的引用,map1和map指向的是同一个对象。这里我们可以通过System.identityHashCode(Object obj)来返回对象内存地址转化后的hashcode,之所以用不直接用map.hashCode(),是因为HashMap的实现类重写了hashCode方法,返回的值不子再是对象内存地址转化的hashcode了。如下,通过输出结果可以看出map和map1指向同一内存地址。

Map<String,String> map = new HashMap<>();
Map<String,String> map1 = map;
map.put("a","A");
System.out.println(map);
System.out.println(map1);
System.out.println(System.identityHashCode(map));
System.out.println(System.identityHashCode(map1));

输出结果

{a=A}
{a=A}
918221580
918221580

3、浅拷贝-map.putAll();

Map<String,String> map = new HashMap<>();
Map<String,String> map1 = new HashMap<>();
map.put("a","A");
map1.putAll(map);
System.out.println(map);
System.out.println(map1);
System.out.println(System.identityHashCode(map));
System.out.println(System.identityHashCode(map1));

输出结果

{a=A}
{a=A}
918221580
2055281021

分析

这里map1通过new初始化,并将map通过putAll()方法将map的key和value复制到map1,所以这里输出时虽然map和map1的内容都是{a=A},但map和map1的内存地址对应的hashCode却不相同,二者在内存中不是同一对象。putAll通过循环map的entrySet调用map1的put()方法将key和value赋值给map1。经过putAll,map1只是对map的key和value分别进行了对象浅拷贝。如下,通过输出结果发现map和map1中的key和value的内存地址都是一致的。

Map<String,String> map = new HashMap<>();
Map<String, String> map1 = new HashMap<>();
map.put("a","A");
map1.putAll(map);
map.keySet().stream().forEach(x-> System.out.println("map的key的内存地址:"+System.identityHashCode(x)));
map1.keySet().stream().forEach(x-> System.out.println("map1的key的内存地址:"+System.identityHashCode(x)));

map.values().stream().forEach(v-> System.out.println("map的值的内存地址:"+System.identityHashCode(v)));
map1.values().stream().forEach(v-> System.out.println("map1的值的内存地址:"+System.identityHashCode(v)));

输出结果

map的key的内存地址:159413332
map1的key的内存地址:159413332
map的值的内存地址:20132171
map1的值的内存地址:20132171

如果我们这里对map1执行put(“a”,“C”),我们会发现这里只是将键“a”指向的对象的内存地址指向了“C”的内存地址,原来“A”的内存地址并没有发生变化。

Map<String,String> map = new HashMap<>();
Map<String, String> map1 = new HashMap<>();
map.put("a","A");
map1.putAll(map);
map.keySet().stream().forEach(x-> System.out.println("map的key的内存地址:"+System.identityHashCode(x)));
map1.keySet().stream().forEach(x-> System.out.println("map1的key的内存地址:"+System.identityHashCode(x)));
map1.put("a","C");
System.out.println("修改map1键a的value为C后,key的内存地址没有变化,但value指向的内存地址发生了变化:");
map.keySet().stream().forEach(x-> System.out.println("map的key的内存地址:"+System.identityHashCode(x)));
map1.keySet().stream().forEach(x-> System.out.println("map1的key的内存地址:"+System.identityHashCode(x)));
map.values().stream().forEach(x-> System.out.println("map的value的内存地址:"+System.identityHashCode(x)));
map1.values().stream().forEach(v-> System.out.println("map1的value的内存地址:"+System.identityHashCode(v)));

输出结果

map的key的内存地址:159413332
map1的key的内存地址:159413332
修改map1键a的value为C后,key的内存地址没有变化,但value指向的内存地址发生了变化:
map的key的内存地址:159413332
map1的key的内存地址:159413332
map的value的内存地址:2094548358
map1的value的内存地址:455896770

4、如何实现深拷贝

如果要拷贝的对象实现了Serializable接口,我们可以通过对象流的输出(out)输入(input)实现对象的深拷贝。

public class CloneUtils {
    @SuppressWarnings("unchecked")
    public static <T extends Serializable> T clone(T obj) {
        T clonedObj = null;
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(obj);
            oos.close();

            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            clonedObj = (T) ois.readObject();
            ois.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return clonedObj;
    }
}

通过CloneUtils工具类实现深拷贝

HashMap<String, String> map = new HashMap<>();
map.put("a", "A");
HashMap<String, String> map1 = CloneUtils.clone(map);
map.keySet().stream().forEach(x -> System.out.println("map的key的内存地址:" + System.identityHashCode(x)));
map1.keySet().stream().forEach(x -> System.out.println("map1的key的内存地址:" + System.identityHashCode(x)));
map.values().stream().forEach(x -> System.out.println("map的value的内存地址:" + System.identityHashCode(x)));
map1.values().stream().forEach(v -> System.out.println("map1的value的内存地址:" + System.identityHashCode(v)));

输出结果

map的key的内存地址:1392838282
map1的key的内存地址:1706234378
map的value的内存地址:804564176
map1的value的内存地址:1342443276

分析

通过对象流的输入输出生成的map1是新的对象,而且map1的key和value对应对象的内存地址也跟map的不一样,这样才算实现了深拷贝。分别对他们key和value进行修改不会受到影响。

注意:文章中举例的key和value都是String,如果大家感觉比较特殊,可以自定义一个对象Person,并实现Serializable进行测试。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值