浅析java的浅拷贝和深拷贝

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

问题描述:map拷贝时发现数据会变化。

!!!!!!!!!!高能预警:
你看到的下面的栗子是不正确的,后面有正确的一种办法,如果需要看的话的,请看到底,感谢各同学的提醒,已做更正,一定要看到最后。

栗子:

public class CopyMap {

      /**
       * @author 张仲华
       * @param args
       * 2014 -8 -6 上午9:29:33
       */
      public static void main(String[] args) {

            Map<String,Integer> map = new HashMap<String,Integer>();
            map.put( "key1", 1);

            Map<String,Integer> mapFirst = map;
            System. out.println( mapFirst);

            map.put( "key2", 2);

            System. out.println( mapFirst);
      }

}

上面程序的期望输出值是,

{key1=1} 
{key1=1}

因为我们只对map添加了2。

但是实际上输出结果为:

{key1=1}
{key2=2, key1=1}

这里是因为map发生了浅拷贝,mapFirst 只是复制了map的引用,和map仍指向同一个内存区域 ,所以,在修改map的时候,mapFirst的值同样会发生变化。
PS:

所谓浅复制:则是只复制对象的引用,两个引用仍然指向同一个对象,在内存中占用同一块内存。被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。
深复制:被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍。

如何解决?
使用深拷贝,拷贝整个对象,而非引用。
Map中有个方法叫做putAll方法,可以实现深拷贝,如下:


public class CopyMap {

       /**
       * @author 张仲华
       * @param args
       * 2014 -8 -6 上午9:29:33
       */
       public static void main(String[] args) {

            Map<String,Integer> map = new HashMap<String,Integer>();
            map.put( "key1", 1);

            Map<String,Integer> mapFirst = new HashMap<String,Integer>();
            mapFirst.putAll(map); //深拷贝

            System. out.println(mapFirst);

            map.put( "key2", 2);

            System. out.println(mapFirst);
      }

}

如上,输出结果为:

{key1=1}
{key1=1}

参考:http://blog.csdn.net/lzkkevin/article/details/6667958

!!!!注意!!!注意!!!!注意!!! 上面并不是深拷贝,留下来的原因是提醒大家,这里是存在错误的。(很高兴你看到这里了)
感谢下面这几位朋友的提醒。
这里写图片描述
这里写图片描述
文章更正如下:
如何实现Map的深拷贝呢?
有一种方法,是使用序列化的方式来实现对象的深拷贝,但是前提是,对象必须是实现了 Serializable接口才可以,Map本身没有实现 Serializable 这个接口,所以这种方式不能序列化Map,也就是不能深拷贝Map。但是HashMap是可以的,因为它实现了 Serializable。下面的方式,基于HashMap来讲,非Map的拷贝。
具体实现如下:

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;
    }
}

如何使用呢,下面是个使用的例子,同时证明了Map的putAll方法并没有实现深拷贝,putAll 仅对基本数据类型起到深拷贝的作用。

public static void main(String[] args) {

        List<Integer> list = new ArrayList<Integer>();
        list.add(100);
        list.add(200);

        HashMap<String,Object> map = new HashMap<String,Object>();
        //放基本类型数据
        map.put("basic", 100);
        //放对象
        map.put("list", list);

        HashMap<String,Object> mapNew = new HashMap<String,Object>();
        mapNew.putAll(map);

        System.out.println("----数据展示-----");
        System.out.println(map);
        System.out.println(mapNew);

        System.out.println("----更改基本类型数据-----");
        map.put("basic", 200);
        System.out.println(map);
        System.out.println(mapNew);

        System.out.println("----更改引用类型数据-----");
        list.add(300);
        System.out.println(map);
        System.out.println(mapNew);


        System.out.println("----使用序列化进行深拷贝-----");
        mapNew = CloneUtils.clone(map);
        list.add(400);
        System.out.println(map);
        System.out.println(mapNew);

    }

输出结果如下:
这里写图片描述
我画了个图:
这里写图片描述
最上面的两条是原始数据,使用了putAll方法拷贝了一个新的mapNew对象;

中间两条,是修改map对象的基本数据类型的时候,并没有影响到mapNew对象。

但是看倒数第二组,更改引用数据类型的时候,发现mapNew的值也变化了,所以putAll并没有对map产生深拷贝。

最后面是使用序列化的方式,发现,更改引用类型的数据的时候,mapNew对象并没有发生变化,所以产生了深拷贝。

上述的工具类,可以实现对象的深拷贝,不仅限于HashMap,前提是实现了Serlizeable接口。

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


总结:(原创)

1. hashMap的put(key,value)方法中,value如果是个引用数据类型(非基本数据类型)的话,存的就是地址。也就是说,如果你接下来还是继续使用原来的对象并对其修改的话,这个hashMap保存的值也是跟着变化的。
那该怎么解决呢?就是不要继续使用原来的对象咯!每次new一个新的出来(重新分配内存空间)。

2. 直接将一个map对象赋值给另一个map对象,两个map指向的是同一个地址。同理,如果你改变其中一个某一(key对应的)value的值,另一个也会跟着改变。

3. map或者hashMap的putAll()方法:对基本数据类型深克隆,对引用对象执行的是浅克隆(指向统一地址)。

4. newHashMap = CloneUtils.clone(oldHashMap)对基本类型和引用类型都是深克隆,即操作oldHashMap中引用类型和基本类型的value的对象,不会引起newHashMap值的改变。注意:map是不能使用的,hashmap才可以,因为hashmap实现了实现 Serializable 这个接口。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在Python中,可以使用copy模块中的deepcopy方法来实现列表的拷贝拷贝会创建一个完全独立的列表副本,无论多少层嵌套,得到的新列表都是和原来无关的。可以通过引入copy模块,并使用copy.deepcopy()来进行拷贝操作。例如: import copy old = [1,[1,2,3],3] new = copy.deepcopy(old) 在上述代码中,old是原始列表,new是拷贝得到的新列表。无论对new进行任何修改,都不会影响到old的值。这种方法是最安全、最清爽、最有效的拷贝方法。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [浅析Python中list的复制及拷贝拷贝](https://download.csdn.net/download/weixin_38643269/12867045)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [Python中列表List的复制(直接复制、拷贝拷贝)](https://blog.csdn.net/weixin_49899130/article/details/129380610)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值