Map复制给新Map时,⽤“=、clone、还是putAll”?论Map的深复制和浅复制⽬录
使⽤场景
在我们最初使⽤map复制开发业务代码时,通常会踩到深浅复制(拷贝)这个坑⾥,⽐如我,在Map复制时
(如:Map<String, String> new_Map = old_Map) 出现过以下两类问题:
1.使⽤Map<String, String> new_Map = old_Map 操作,当修改new_Map属性后,old_Map属性也跟着变了,但我并没有修改过
old_Map;
2.由于Map中的value值不仅有基本数据类型,还有引⽤数据类型,所以当我修改引⽤类型属性后,new_Map和old_Map的引⽤变量值都发⽣变化;(如你的value都是基本类型,就不涉及深浅拷贝的问题)
尝试过的办法
1. “=”赋值
新建⼀个Map,然后使⽤“=”直接赋值,这样只是复制了old_Map的引⽤,和old_Map仍使⽤同⼀个内存区域,所以,在修改new_Map的时候,old_Map的值同样会发⽣变化。
<Map<String, String> new_Map = old_Map>
上述的办法不⾏,使⽤Map本⾝提供的⽅法,⽹上⼤都说putAll()和clone()⽅法就是深拷贝,但是实际使⽤后,发现前后Map中的引⽤对象还是都被改变了;这⾥就是开头说到的,这两个⽅法只能修改基本数据类型的,如果是引⽤类型不⾏,这两个⽅法是浅拷贝!
来,让我们⼀起跟⼀下源码↓↓↓
2. 使⽤.putAll()⽅法
创建⼀个新的Map结构,使⽤putAll()⽅法把原先的Map添加到新的Map中,但是发现修改了副本的Map之后,原先的Map中数据也被修改了;(源码如下)
public void putAll(Map<?extends K,?extends V> m){
putMapEntries(m,true);// 调⽤了putMapEntries⽅法
}
final void putMapEntries(Map<?extends K,?extends V> m,boolean evict){
int s = m.size();
if(s >0){
if(table == null){// pre-size
float ft =((float)s / loadFactor)+1.0F;
int t =((ft <(float)MAXIMUM_CAPACITY)?
(int)ft : MAXIMUM_CAPACITY);
if(t > threshold)
threshold =tableSizeFor(t);
}
else if(s > threshold)
resize();
for(Map.Entry<?extends K,?extends V> e : m.entrySet()){
K key = e.getKey();
V value = e.getValue();
putVal(hash(key), key, value,false, evict);// 循环调⽤了value,但value中的引⽤对象指针并没有改变。
// 扩展:map.put("key","value")的put()也是调⽤了putVal()⽅法
}
}
}
3. 使⽤.clone()⽅法
HashMap⾃带了⼀个clone()⽅法,但是,它的源码中注释说明了也只是⼀种浅复制(拷贝):(源码如下)
@Override
public Object clone(){
HashMap<K,V> result;
try{
result =(HashMap<K,V>)super.clone();
}catch(CloneNotSupportedException e){
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
result.putMapEntries(this,false);// 可见,和putAll调⽤了同⼀个接⼝,
return result;
}
final void putMapEntries(Map<?extends K,?extends V> m,boolean evict){
int s = m.size();
if(s >0){
if(table == null){// pre-size
float ft =((float)s / loadFactor)+1.0F;
int t =((ft <(float)MAXIMUM_CAPACITY)?
(int)ft : MAXIMUM_CAPACITY);
if(t > threshold)
threshold =tableSizeFor(t);
}
else if(s > threshold)
resize();
for(Map.Entry<?extends K,?extends V> e : m.entrySet()){
K key = e.getKey();
V value = e.getValue();
putVal(hash(key), key, value,false, evict);// 同上,循环调⽤了“value”,value中的引⽤对象指针并没有改变
}
}
}
测试⽤例
List<Integer> list =new ArrayList<Integer>();
list.add(100);
list.add(200);
HashMap<String,Object> old_map =new HashMap<String,Object>();
old_map.put("name","蔡虚坤");//放基本类型数据
old_map.put("list", list);//放对象
HashMap<String,Object> new_map =new HashMap<String,Object>();
new_map.putAll(old_map);
System.out.println("----基础数据展⽰-----");
System.out.println("old: "+ old_map);
System.out.println("new: "+ new_map);
System.out.println("----更改基本数据类型的数据-----");
old_map.put("name","娘炮");
System.out.println("old: "+ old_map);
System.out.println("new: "+ new_map);
System.out.println("----更改引⽤类型的数据-----");
list.add(300);
System.out.println("old: "+ old_map);
System.out.println("new: "+ new_map);
System.out.println("----使⽤序列化进⾏深拷贝⾃定义Clone⽅法-----");
new_map =myClone(old_map);// myClone() ⽅法源码在下⽅↓↓
list.add(400);
System.out.println("old: "+ old_map);
System.out.println("new: "+ new_map);
输出结果:
Connected to the target VM, address:'127.0.0.1:58242', transport:'socket'
----基础数据展⽰-----
old:{name=蔡虚坤, list=[100,200]}
new:{name=蔡虚坤, list=[100,200]}
----更改基本数据类型的数据-----
old:{name=娘炮, list=[100,200]}
new:{name=蔡虚坤, list=[100,200]}
-
---更改引⽤类型的数据-----
old:{name=娘炮, list=[100,200,300]}
new:{name=蔡虚坤, list=[100,200,300]}
----使⽤序列化进⾏深拷贝-----
old:{name=娘炮, list=[100,200,300,400]}
new:{name=娘炮, list=[100,200,300]}
#最上⾯的两条是原始数据,使⽤了putAll⽅法拷贝了⼀个新的new_map对象,
#中间两条,是修改old_map对象的基本数据类型的时候,并没有影响到new_map对象。
#但是看倒数第⼆组,更改引⽤数据类型的时候,发现new_map的值也变化了,所以putAll并没有对old_map产⽣深拷贝。
#最后⾯是使⽤序列化的⽅式,发现,更改引⽤类型的数据的时候,new_map对象并没有发⽣变化,所以产⽣了深拷贝。(下⽅提供⾃定义clone⽅法源码)
#上述的⼯具类,可以实现对象的深拷贝,不仅限于HashMap,前提是实现了Serlizeable接⼝。
测试⽤例源码
package com.softsec.demo;
import java.io.*;
import java.util.*;
public class demoMap implements Cloneable{
public static void main(String[] srag){
List<Integer> list =new ArrayList<Integer>();
list.add(100);
list.add(200);
HashMap<String,Object> old_map =new HashMap<String,Object>();
old_map.put("name","蔡虚坤");//放基本类型数据
old_map.put("list", list);//放对象
HashMap<String,Object> new_map =new HashMap<String,Object>();
new_map.putAll(old_map);
System.out.println("----基础数据展⽰-----");
System.out.println("old:"+ old_map);
System.out.println("new:"+ new_map);
System.out.println("----更改基本数据类型的数据-----");
old_map.put("name","娘炮");
System.out.println("old:"+ old_map);
System.out.println("new:"+ new_map);
System.out.println("----更改引⽤类型的数据-----");
list.add(300);
System.out.println("old:"+ old_map);
System.out.println("new:"+ new_map);
System.out.println("----使⽤序列化进⾏深拷贝⾃定义Clone⽅法-----");
new_map =myClone(old_map);
list.add(400);
System.out.println("old:"+ old_map);
System.out.println("new:"+ new_map);
}
/**
* ⾃定义clone⽅法(对象必须是实现了Serializable接⼝)
*
*/
public static<T extends Serializable> T myClone(T obj){
T clonedObj = null;
try{
ByteArrayOutputStream baos =new ByteArrayOutputStream();
ObjectOutputStream oos =new ObjectOutputStream(baos);
oos.writeObject(obj);
oos.close();
ByteArrayInputStream bais =new ByteArray()); ObjectInputStream ois =new ObjectInputStream(bais);
clonedObj =(T) adObject();
ois.close();clone
}catch(Exception e){
e.printStackTrace();
}
return clonedObj;
}
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论