Map深clone
近来看到很多的关于Map深克隆的文章,可谓是良莠不齐; 有的文章分析的比较好,但是给出的例子却不对。在我所看的文章的例子几乎都用String类作为演示例子。测试出来的结果貌似是对了,但是这个结果却蒙蔽了大家,就连原文作者也没有发现。值得注意的是:String这个类是非常特殊!从API中可以看到类的声明:public final class String ;注意是final修饰的,改变它的值,实则它的引用地址也发生了改变,下面我们看一下例子:
①String aStr = "a";
②String bStr = "a";
③bStr = "c";
当程序执行完①时,内存中将保存“a”并将aStr指向这个内存地址;但执行到②时,注意,此时并没有开辟新的内存,而是将bStr直接指向“a”;因此aStr与bStr一定是完全相等的(注意,是完全相等;这个例子也可以区分‘equals’与‘==’的区别。)但程序执行到③时,内存就重新开辟空间保存“c” 此时bStr就指向“c”的内存地址。我们输出bStr确实看到bStr发生了改变,但是并没有改变原来bStr所指地址的那个值,也就是说并没有将“a”改成“c”,而是将bStr直接指向另一个引用地址,因此我们往往误以为bStr的‘值(地址和值)’改变了。 不打算继续说了,估计大家被绕晕了。简而言之就是:你一旦改变了String的值就意味着它所指向的内存地址也就发生了变化(这个原理同样也可以解释声明String类型变量时建议直接用“=String”而不是用“=new String()”的方式声明,以及String类型变量不要用累加次数过多(一般是3次或3次以下))。所以那些文章用String做演示是犯了一个非常低级的错误! 若用其它的类(比如自定义的JavaBean)代替String的话你就发现,其实任然还只是浅克隆,而大费周章的所重写的clone方法然与Object里面继承过来的方法结果仍然相同,可谓是白费了一番心思!
下面本人写的一个Map深克隆的例子,仍然是通过重写clone方法来实现的,供给大家参考使用:
JAVABean , Map格式:Map<String,User>
/**
* @author TDW
*
*/
publicclassUser implementsSerializable{
privatestaticfinallong serialVersionUID =1L;
privateString id;
privateString userName;
privateString password;
privateString nickName;
privateString[] hobby;
//get、set method 略
}
deep clone test :
package com.study.demo.clone;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import com.study.api.bean.User;
/**
* Map deep clone test
* @author TDW
*
*/
publicclassMapCloneTest{
class customHashMap extendsHashMap<Object,Object>{
privatestaticfinallong serialVersionUID =1L;
public customHashMap(){
super();
}
public customHashMap(int initialCapacity){
super(initialCapacity);
}
publicObject clone(){
Map<Object,Object> target =newHashMap<Object,Object>();
try{
ByteArrayOutputStream byteout =null;
ObjectOutputStreamout=null;
Object obj =null;
for(Iterator<Object> keyIt =this.keySet().iterator(); keyIt
.hasNext();){
Object key = keyIt.next();
byteout =newByteArrayOutputStream();
out=newObjectOutputStream(byteout);
out.writeObject(this.get(key));
ByteArrayInputStream bytein =newByteArrayInputStream(
byteout.toByteArray());
ObjectInputStreamin=newObjectInputStream(bytein);
obj =in.readObject();// 反序列化出来,就相当于将原来的值和地址(即引用)copy了一份
target.put(key, obj);// 习惯上一般key是不做修改的,因此没有copy key的必要
// ,倘若有必要则同样反序列化出来就行了(如果是String类型也无需反序列化,上面已经解释的很清楚了)
}
} catch(Exception e){
e.printStackTrace();
}
return target;
}
}
publicstaticvoid main(String[] args){
User user1 =newUser();
user1.setId("11111111111111");
User user2 =newUser();
user2.setId("22222222222222");
customHashMap source =(newMapCloneTest()).new customHashMap();
source.put("key1", user1);
source.put("key2", user2);
System.out.println(source);
Map target =(Map) source.clone();
User user =(User) target.get("key1");
user.setId("xxxxxxxxxxxxxx");// 改变副本的值
System.out.println(target);// 对比结果
System.out.println(source);// 源,没有被修改
}
}
输出结果大家自行对比。
不仅如此,List也可以用此方式进行深克隆,凡是实现过序列号接口的都可以通过此方式进行深克隆,下面是list深克隆例子:
/**
* List深度clone
* @param src
* @return
*/
publicstaticArrayList<Object> copyList(ArrayList<Object> src){
ArrayList<Object> list =null;
try{
ByteArrayOutputStream byteout =newByteArrayOutputStream();
ObjectOutputStreamout=newObjectOutputStream(byteout);
out.writeObject(src);
ByteArrayInputStream bytein =newByteArrayInputStream(byteout.toByteArray());
ObjectInputStreamin=newObjectInputStream(bytein);
list =(ArrayList<Object>)in.readObject();
}catch(Exception e){
System.out.println("cache copy error !");
e.printStackTrace();
}
return list;
}