Java几种深度拷贝方法效率比较
程序员文章站
2023-08-12 11:04:50
Java在复制一个对象时有浅拷贝与深拷贝之分,具体区别就不在此赘述,本文主要分析Java深拷贝的几种方法以及他们的效率高低。 1. 使用Java序列化方法 想要深拷贝一个对象,常用的方法是序列化为数据流,此方法的前提是对象以及对象中包含的子对象都要继承Serializable接口。 2. 利用Kry ......
java在复制一个对象时有浅拷贝与深拷贝之分,具体区别就不在此赘述,本文主要分析java深拷贝的几种方法以及他们的效率高低。
1. 使用java序列化方法
想要深拷贝一个对象,常用的方法是序列化为数据流,此方法的前提是对象以及对象中包含的子对象都要继承serializable接口。
2. 利用kryo序列化框架
kryo是一个快速高效的java序列化框架,旨在提供快速、高效和易用的api。无论文件、数据库或网络数据kryo都可以随时完成序列化。kryo还可以执行自动深拷贝(克隆)、浅拷贝(克隆)。这是对象到对象的直接拷贝,非对象->字节->对象的拷贝。该方法不需要继承serializable接口。
<dependency> <groupid>com.esotericsoftware</groupid> <artifactid>kryo</artifactid> <version>4.0.1</version> </dependency>
3. 利用json转化的方法
如果对象没有继承serializable接口,可以先将对象转化为json,再序列化为对象,和第一种方法类似。json转换工具可以用jackson或者json-lib,本文选择json-lib只需要在maven里面添加以下依赖:
<dependency> <groupid>net.sf.json-lib</groupid> <artifactid>json-lib</artifactid> <version>2.4</version> <classifier>jdk15</classifier> </dependency>
4. 手动new对象的方法
人工构建对象,如果需要复制的对象中包含非基本类型,如list,对象等结构时,可以在需要的时候手动new对象,将属性值挨个调用set方法,比较繁琐。
5. 具体实例
(1) 首先构造两个实体对象user.java和name.java,具体代码如下
public class user implements serializable{ private static final long serialversionuid = -6952319891279734655l; private name name; private string phone; private string sex; private int age; ...//此处省略get、set方法 }
public class name implements serializable{ private static final long serialversionuid = -2023200990550843496l; private string firstname; private string lastname; ...//此处省略get、set方法 }
(2) 测试的主程序代码如下:
package com.test.sort.hello; /** * created by geekboy on 2017/12/10. */ import java.io.bytearrayinputstream; import java.io.bytearrayoutputstream; import java.io.objectinputstream; import java.io.objectoutputstream; import com.esotericsoftware.kryo.kryo; import net.sf.json.jsonobject; /** * @author geekboy * */ public class testcopy { public static void main(string[] args) { user source=new user(); source.setage(25); source.setphone("13590117892"); source.setsex("1"); name name=new name(); name.setfirstname("li"); name.setlastname("ming"); source.setname(name); long before=system.currenttimemillis(); for(int i=0;i<1000000;i++){ user tmp=copybynewobject(source); try { //user tmp=copyimplserializable(source); //user tmp=copybyjson(source); } catch (exception e) { e.printstacktrace(); } } long after=system.currenttimemillis(); system.out.println(after-before); } /** * 深层拷贝 - 需要类继承序列化接口 * @param <t> * @param obj * @return * @throws exception */ @suppresswarnings("unchecked") public static <t> t copyimplserializable(t obj) throws exception { bytearrayoutputstream baos = null; objectoutputstream oos = null; bytearrayinputstream bais = null; objectinputstream ois = null; object o = null; //如果子类没有继承该接口,这一步会报错 try { baos = new bytearrayoutputstream(); oos = new objectoutputstream(baos); oos.writeobject(obj); bais = new bytearrayinputstream(baos.tobytearray()); ois = new objectinputstream(bais); o = ois.readobject(); return (t) o; } catch (exception e) { throw new exception("对象中包含没有继承序列化的对象"); } finally{ try { baos.close(); oos.close(); bais.close(); ois.close(); } catch (exception e2) { //这里报错不需要处理 } } } /** * 深层拷贝 - 需要net.sf.json.jsonobject * @param <t> * @param obj * @return * @throws exception */ @suppresswarnings("unchecked") public static <t> t copybyjson(t obj) throws exception { return (t)jsonobject.tobean(jsonobject.fromobject(obj),obj.getclass()); } /** * 通过new 对象的方法深拷贝 * @param source * @return */ public static user copybynewobject(user source){ user result=new user(); result.setage(source.getage()); result.setphone(source.getphone()); result.setsex(source.getsex()); name name=new name(); name.setfirstname(source.getname().getfirstname()); name.setlastname(source.getname().getlastname()); result.setname(name); return result; } /** * 通过kryo框架深拷贝 * @param source * @return */ public static user copybykryo(user source){ kryo kryo = new kryo(); return kryo.copy(source); } }
6. 运行结果及效率分析
new 对象 | jdk序列化 | kyro序列化 | json转化 | |
---|---|---|---|---|
1000次 | 1 | 237 | 307 | 477 |
10000次 | 3 | 914 | 790 | 1328 |
100000次 | 10 | 2951 | 1780 | 2604 |
通过上面的测试可以看出new 对象的方法是最快的,比较适合性能要求较高的场合。其次是kyro序列化方法,json转化的方法还有优化的余地,使用不同的json库会有不同结果。最慢的是jdk的序列化操作,不建议用此种方案进行深度拷贝。