问题: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);
      }
 
}
 
上面程序的期望输出值是,mapFrist的值均为1,
但是实际上输出结果为:
{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的拷贝。

具体实现如下:

01 public class CloneUtils {
02  
03     @SuppressWarnings("unchecked")
04     public static <T extends Serializable> T clone(T obj){
05          
06         T clonedObj = null;
07         try {
08             ByteArrayOutputStream baos = new ByteArrayOutputStream();
09             ObjectOutputStream oos = new ObjectOutputStream(baos);
10             oos.writeObject(obj);
11             oos.close();
12              
13             ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
14             ObjectInputStream ois = new ObjectInputStream(bais);
15             clonedObj = (T) ois.readObject();
16             ois.close();
17              
18         }catch (Exception e){
19             e.printStackTrace();
20         }
21          
22         return clonedObj;
23     }
24 }

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

栗子:

01 public static void main(String[] args) {
02          
03         List<Integer> list = new ArrayList<Integer>();
04         list.add(100);
05         list.add(200);
06          
07         HashMap<String,Object> map = new HashMap<String,Object>();
08         //放基本类型数据
09         map.put("basic"100);
10         //放对象
11         map.put("list", list);
12          
13         HashMap<String,Object> mapNew = new HashMap<String,Object>();
14         mapNew.putAll(map);
15          
16         System.out.println("----数据展示-----");
17         System.out.println(map);
18         System.out.println(mapNew);
19          
20         System.out.println("----更改基本类型数据-----");
21         map.put("basic"200);
22         System.out.println(map);
23         System.out.println(mapNew);
24          
25         System.out.println("----更改引用类型数据-----");
26         list.add(300);
27         System.out.println(map);
28         System.out.println(mapNew);
29          
30          
31         System.out.println("----使用序列化进行深拷贝-----");
32         mapNew = CloneUtils.clone(map);
33         list.add(400);
34         System.out.println(map);
35         System.out.println(mapNew);
36          
37     }

输出结果如下:

最上面的两条是原始数据,使用了putAll方法拷贝了一个新的mapNew对象,

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

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

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

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

还没有看putAll的源码实现,后面看下为什么不能实现深拷贝。

Map拷贝 关于对象深拷贝 浅拷贝的问题的更多相关文章

  1. Java对象深拷贝浅拷贝总结

    目录 深拷贝 1. 手动new 2. clone方法 3. java自带序列化 4. json序列化 性能测试 深拷贝总结 浅拷贝 1. spring BeanUtils(Apache BeanUti ...

  2. ios深拷贝,浅拷贝,拷贝自定义对象的简单介绍(转)

    copy语法的目的:改变副本的时候,不会影响到源对象: 深拷贝:内容拷贝,会产生新的对象.新对象计数器置为1,源对象计数器不变. 浅拷贝:指针拷贝,不会产生新的对象.源对象计数器+1. 拷贝有下面两个 ...

  3. Object对象的浅拷贝与深拷贝方法详解

    /* ===================== 直接看代码 ===================== */ <!DOCTYPE html> <html> <head& ...

  4. java 将Map拷贝到另一个Map对象当中

      java 将Map拷贝到另一个Map对象当中 CreateTime--2018年6月4日09点46分 Author:Marydon 1.需求说明 将一个MapA对象中所有的键值对完全拷贝到另一个M ...

  5. 也来玩玩 javascript对象深拷贝,浅拷贝

    经常看到讨论c#深拷贝,浅拷贝的博客,最近js写的比较多, 所以也来玩玩js的对象拷贝. 下面是维基百科对深浅拷贝的解释: 浅拷贝 One method of copying an object is ...

  6. C++拷贝构造函数(深拷贝,浅拷贝)

    对于普通类型的对象来说,它们之间的复制是很简单的,例如:int a=88;int b=a; 而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量.下面看一个类对象拷贝的简单例子. #i ...

  7. javascript对象深拷贝,浅拷贝 ,支持数组

    javascript对象深拷贝,浅拷贝 ,支持数组 经常看到讨论c#深拷贝,浅拷贝的博客,最近js写的比较多, 所以也来玩玩js的对象拷贝. 下面是维基百科对深浅拷贝的解释: 浅拷贝 One meth ...

  8. C++拷贝构造函数(深拷贝与浅拷贝)

    转自http://blog.csdn.net/lwbeyond/article/details/6202256/ 一. 什么是拷贝构造函数 对于普通类型的对象来说,它们之间的复制是很简单的,例如:in ...

  9. Java对象的浅拷贝和深拷贝&&String类型的赋值

    Java中的数据类型分为基本数据类型和引用数据类型.对于这两种数据类型,在进行赋值操作.方法传参或返回值时,会有值传递和引用(地址)传递的差别. 浅拷贝(Shallow Copy): ①对于数据类型是 ...

随机推荐

  1. HDU-1233 还是畅通工程

    Problem Description 某省调查乡村交通状况,得到的统计表中列出了任意两村庄间的距离.省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能 ...

  2. 夺命雷公狗----Git---2---基本用法

    首先我们来创建一个git仓库,其实命令也是很简单的.. git init 其实在创建完成后,该目录会多了一个.git的文件夹,但该文件夹是隐藏的.... 其实这就是一个git仓库,特别建议不要在这个目 ...

  3. 【原创】我所理解的自动更新-外网web服务器配置

    ClientDownload和ClientUpdate共享渠道配置信息: channel-0.php //以appstore的渠道为例 <?php define('APPNAME', 'TOKE ...

  4. hdu 3839 Ancient Messages (dfs )

    题目大意:给出一幅画,找出里面的象形文字. 要你翻译这幅画,把象形文字按字典序输出. 思路:象形文字有一些特点,分别有0个圈.1个圈.2个圈...5个圈.然后dfs或者bfs,就像油井问题一样,找出在 ...

  5. GPS经纬度换算成XY坐标

    ;        }

  6. ebs history to look for password

    -bash-3.2$ ps -ef|grep tns root 101 2 0 Jun01 ? 00:00:00 [netns] oravibs 10625 1 0 Sep09 ? 00:01:03 ...

  7. 主机和虚拟机不能ping通问题

    VMware的三种网络模式附上: 在vmw中,默认有3个虚拟交换机 分别是VMnet0(桥接).VMnet1(主机网络)以及VMnet8(NAT) 还可以根据需要添加VMnet2~VMnet7和VMn ...

  8. mysql共享表空间转独立表空间

    使用innodb_export_import.py脚本: https://github.com/thecpaneladmin/innodb-tools 安装MySQL-python模块: shell ...

  9. edittext把软键盘上的回车键改为搜索、发送或者 下一步,窗口随软键盘弹出而改变。

    http://m.blog.csdn.net/article/details?id=51350501 以上博文讲解很详细. 如图所示,有时候为了布局美观,在搜索时没有搜索按钮,而是调用软件盘上的按钮. ...

  10. python查找空格和中文

    前言 图片或者文件夹下,命名不规范,有中文或者有空格.这个脚本批处理查找,并输出到 txt中方便修改,也可以扩展为 直接脚本删除空格等.目前只用在Windows上,mac没有测试,不知道能不能行,有需 ...