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);
      }
 
}
 
上面程序的期望輸出值是,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

 

注意!!!注意!!!!注意!!! 上面並不是深拷貝,留下來的原因是提醒大家,這里是存在錯誤的。(很高興你看到這里了)

感謝下面這幾位朋友的提醒。

QQ20160318-0.pngQQ20160318-1.png

 文章更正如下:

如何實現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     }

 

輸出結果如下:

QQ20160318-2.png

 

最上面的兩條是原始數據,使用了putAll方法拷貝了一個新的mapNew對象,

中間兩條,是修改map對象的基本數據類型的時候,並沒有影響到mapNew對象。

但是看倒數第二組,更改引用數據類型的時候,發現mapNew的值也變化了,所以putAll並沒有對map產生深拷貝。

最后面是使用序列化的方式,發現,更改引用類型的數據的時候,mapNew對象並沒有發生變化,所以產生了深拷貝。

上述的工具類,可以實現對象的深拷貝,不僅限於HashMap,前提是實現了Serlizeable接口。

 

還沒有看putAll的源碼實現,后面看下為什么不能實現深拷貝。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM