Java深度拷贝方式和性能对比

2022-12-30 0 1,028

序言

Java的广度复本大体分成布季夫(同时实现Java的CloneUSB)和格式化(同时实现Java的SerializableUSB)三种,但如前所述相同的格式化形式,有能延展出三种形式。上面预测呵呵五种的小常识和操控性对照【现阶段笔记本电脑为4核16G,而已现阶段采用main形式Renderscript试验】。

一、复本和厚薄复本

能采用Java native形式提供更多的Clone形式展开第一类的复本,其操控性是最低的,即使高过new URL。采用newURL建立第一类,假如是第二次建立则会历经类读取监督机制的父母亲指派(读取、校正、预备、导出、初始化)。即便非第二次建立也会历经(自变量池推论,缓存再次分配,值初始化,init形式初始化,栈中第一类的提及等)等操作过程。

他们须要承继自CloneUSB,改写Object的clone形式。如下表所示:

public class DeepCopyEntity implements Cloneable { @Override protected DeepCopyEntity clone() { try { return (DeepCopyEntity)super.clone(); } catch (CloneNotSupportedException e) { log.info(“没同时实现布季夫USB”); return null; } } }

但他们在采用的这时候,须要每一第一类都撰写这种的标识符。能强化为承继自类似于上面的 CloneSupport<T> 类(前体是没承继其它的类):

public class CloneSupport<T> implements Cloneable { @SuppressWarnings(“unchecked”) @Override public T clone() { try { return (T) super.clone(); } catch (CloneNotSupportedException e) { throw new CloneRuntimeException(e); } } }

但即便是布季夫后的第一类也是浅复本。即第一类的特性假如亦然基本上正则表达式和String的情况下,新老第一类的第一类特性的物理地址吴垂昆完全相同,则任何人两个第一类发生改变其值后,另两个第一类的值也是发生改变了,这许多这时候可能将是他们不想的。所以须要展开广度的复本。则须要其特性第一类的类也承继自CloneUSB,因此再次clone形式。如下表所示(是我工程项目中采用的):

public class PurOrderSkuBO implements Serializable, Cloneable { @Override public PurOrderSkuBO clone() { try { final PurOrderSkuBO clone = (PurOrderSkuBO) super.clone(); clone.purOrderSkuDTO = purOrderSkuDTO.clone(); clone.productList = productList.stream().map(PurOrderItemBO::clone).collect(Collectors.toList()); return clone; } catch (CloneNotSupportedException e) { return new PurOrderSkuBO(); } } private PurOrderSkuDTO purOrderSkuDTO; private List<PurOrderItemBO> productList; } public class PurOrderSkuDTO extends CloneSupport<PurOrderSkuDTO> { }

二、格式化

另一种同时实现广度复本的形式是格式化,无论是Jdk的格式化还是其它形式的格式化都须要同时实现自 java.io.SerializableUSB,因此设置自己的serialVersionUID,因此保证工程项目中不能有完全相同的值(许多开发的这时候,如前所述原来的类copy过来后须要展开修改),如下表所示:

public class DeepCopyEntity implements Cloneable, Serializable { private static final long serialVersionUID = 6172279441386879379L; }

三、广度复本的形式

1、newURL

同时实现第一类的广度复本,是第一类的每一层特性的物理地址都不完全相同,所以如前所述new 第一类,再每一层设置new的特性第一类。也是能同时实现的,或者如前所述反射的形式,因此操控性也是比较高的。须要注意jdk 6及之前的反射操控性比较差。

优点:操控性高,缺点:是每一第一类都须要new,因此每一层都须要用setter等展开赋值【硬编码】。

2、Clone

优点:操控性高,缺点:所有层级只要有特性第一类就须要同时实现Clone,因此改写clone形式。假如第一类有七八层,其中每一层的每两个地方没注意到就可能将非深复本。

3、jdk格式化

jdk格式化只须要如前所述ObjectOutputStream将原第一类流写出去(写入本地磁盘),再如前所述ObjectInputStream将第一类流读回来即可。如下表所示:

/** * 深层复本 – 须要类承继格式化USB * @param <T> 第一类类型 * @param obj 原第一类 * @return 广度复本的第一类 * @throws Exception * @see java.io.Closeable * @see AutoCloseable 不用展开关闭 */ @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; //假如子类没承继该USB,这一步会报错 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(“第一类中包含没承继格式化的第一类”); } }

优点:不须要像布季夫和new一样单独开发,缺点:操控性比较差

4、kyro格式化

kyro须要单独引入maven依赖,如:

<dependency> <groupId>com.esotericsoftware</groupId> <artifactId>kryo</artifactId> <version>5.0.0-RC9</version> </dependency>

采用时须要建立 Kryo第一类【 Kryo kryo = new Kryo(); 】,而已该第一类亦然线程安全的,所有假如在工程项目中采用时,最好放到ThreadLocal中展开建立。采用就比较简单了:

public static <T> T copyByKryo(T source){ return kryo.copy(source); }

优点:操控性较高, 缺点:须要单独引入maven,操控性比new 和clone的低一点

5、Json格式化

工程项目上采用Json 展开 redis、rpc初始化(如 Spring Cloud Feign) 展开格式化和反格式化是比较常用的,但假如仅仅是本地广度复本,则采用该形式操控性是最差的。能在上面展开比较,各种json框架的格式化形式都差不多。

四、操控性对照

建立两个50个字段的第一类,并采用相同的广度复本形式,建立第一类N多遍。

@Data @NoArgsConstructor @AllArgsConstructor public class DeepCopyEntity implements Cloneable, Serializable { /** * 格式化标识 */ private static final long serialVersionUID = 6172279441386879379L; @Override protected DeepCopyEntity clone() { try { return (DeepCopyEntity)super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); return null; } } private String id; private String field1; private String field2; private String field3; private String field4; private String field5; private String field6; private String field7; private String field8; private String field9; private String field10; private String field11; private String field12; private String field13; private String field14; private String field15; private String field16; private String field17; private String field18; private String field19; private String field20; private String field21; private String field22; private String field23; private String field24; private String field25; private String field26; private String field27; private String field28; private String field29; private String field30; private String field31; private String field32; private String field33; private String field34; private String field35; private String field36; private String field37; private String field38; private String field39; private String field40; private String field41; private String field42; private String field43; private String field44; private String field45; private String field46; private String field47; private String field48; private String field49; private String field50; } package com.kevin.deepcopy; import com.esotericsoftware.kryo.Kryo; import net.sf.json.JSONObject; import org.springframework.util.StopWatch; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; /** * 广度复本类型 循环次数[1000] 循环次数[10000] 循环次数[1000000] * new 5 ms 14 ms 133 ms * * Cloneable: < 1 ms 7 ms 88 ms * * Jdk格式化: 272 ms 1589 ms 66190 ms * * Kryo格式化: 95 ms 123 ms 2438 ms * * Json格式化: 1203 ms 3746 ms 163512 ms * * 总结: 1)、格式化操控性 Clone > new > Kryo格式化 > Jdk格式化 > Json(各种Json类似于)格式化 * 2)、Clone深复本操控性最低,但假如特性中有特定的第一类字段,则须要自己撰写标识符 * 3)、new 操控性仅次于Clone,因为须要执行Jvm操作过程(自变量池推论,缓存再次分配,值初始化,init形式初始化,栈中第一类的提及等),因此主要是每一第一类须要单独撰写标识符,当然也不建议采用反射 * 4)、kryo 操控性较高,因此不须要单独的开发, 若对操控性不是特别高,能考虑采用.(kryo亦然线程安全的,工程项目中采用时能放入ThreadLocal中) * 5)、Jdk格式化和Json格式化,操控性太低,高操控性工程项目不建议采用 * * 总结的总结: 假如操控性要求特别高(或者第一类结构层次不深),能采用Clone形式;否则能考虑采用 Kryo格式化和反格式化同时实现第一类深复本 * * @author kevin * @date 2020/9/27 13:45 * @since 1.0.0 */ public class DeepCopyTest { /** * 循环的次数 */ private static final int LOOP = 1000; private static Kryo kryo = new Kryo(); public static void main(String[] args) throws Exception { DeepCopyEntity demo = getInit(); StopWatch stopWatch = new StopWatch(“试验深复本”); stopWatch.start(); for (int i = 0; i < LOOP; i++) { // DeepCopyEntity deep = newObject(demo); final DeepCopyEntity deep = demo.clone(); // final DeepCopyEntity deepCopyEntity = copyImplSerializable(demo); // final DeepCopyEntity deepCopyEntity = copyByKryo(demo); // final DeepCopyEntity deepCopyEntity1 = copyByJson(demo); } stopWatch.stop(); System.out.println(stopWatch.prettyPrint()); } /** * 深层复本 – 须要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()); } /** * * @param source * @return */ public static DeepCopyEntity copyByKryo(DeepCopyEntity source){ return kryo.copy(source); } /** * 深层复本 – 须要类承继格式化USB * @param <T> * @param obj * @return * @throws Exception * @see java.io.Closeable * @see AutoCloseable 不用展开关闭 */ @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; //假如子类没承继该USB,这一步会报错 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(“第一类中包含没承继格式化的第一类”); } } private static DeepCopyEntity newObject(DeepCopyEntity demo) { final DeepCopyEntity deepCopyEntity = new DeepCopyEntity(); deepCopyEntity.setId(demo.getId()); deepCopyEntity.setField1(demo.getField1()); deepCopyEntity.setField2(demo.getField2()); deepCopyEntity.setField3(demo.getField1()); deepCopyEntity.setField4(demo.getField1()); deepCopyEntity.setField5(demo.getField1()); deepCopyEntity.setField6(demo.getField1()); deepCopyEntity.setField7(demo.getField1()); deepCopyEntity.setField8(demo.getField1()); deepCopyEntity.setField9(demo.getField1()); deepCopyEntity.setField10(demo.getField1()); deepCopyEntity.setField11(demo.getField1()); deepCopyEntity.setField12(demo.getField1()); deepCopyEntity.setField13(demo.getField1()); deepCopyEntity.setField14(demo.getField1()); deepCopyEntity.setField15(demo.getField1()); deepCopyEntity.setField16(demo.getField1()); deepCopyEntity.setField17(demo.getField1()); deepCopyEntity.setField18(demo.getField1()); deepCopyEntity.setField19(demo.getField1()); deepCopyEntity.setField20(demo.getField1()); deepCopyEntity.setField21(demo.getField1()); deepCopyEntity.setField22(demo.getField1()); deepCopyEntity.setField23(demo.getField1()); deepCopyEntity.setField24(demo.getField1()); deepCopyEntity.setField25(demo.getField1()); deepCopyEntity.setField26(demo.getField1()); deepCopyEntity.setField27(demo.getField1()); deepCopyEntity.setField28(demo.getField1()); deepCopyEntity.setField29(demo.getField1()); deepCopyEntity.setField30(demo.getField1()); deepCopyEntity.setField31(demo.getField1()); deepCopyEntity.setField32(demo.getField1()); deepCopyEntity.setField33(demo.getField1()); deepCopyEntity.setField34(demo.getField1()); deepCopyEntity.setField35(demo.getField1()); deepCopyEntity.setField36(demo.getField1()); deepCopyEntity.setField37(demo.getField1()); deepCopyEntity.setField38(demo.getField1()); deepCopyEntity.setField39(demo.getField1()); deepCopyEntity.setField40(demo.getField1()); deepCopyEntity.setField41(demo.getField1()); deepCopyEntity.setField42(demo.getField1()); deepCopyEntity.setField43(demo.getField1()); deepCopyEntity.setField44(demo.getField1()); deepCopyEntity.setField45(demo.getField1()); deepCopyEntity.setField46(demo.getField1()); deepCopyEntity.setField47(demo.getField1()); deepCopyEntity.setField48(demo.getField1()); deepCopyEntity.setField49(demo.getField1()); deepCopyEntity.setField50(demo.getField1()); */ private static DeepCopyEntity getInit() { final DeepCopyEntity deepCopyEntity = new DeepCopyEntity(); deepCopyEntity.setId(“试验字段进来撒个是个是个复活节快乐时刻六公里按时交付格拉斯可根据ask了接受了嘎嘎健康金克拉是个零售价格克拉斯关键时刻两个jklsghbld时间噶设立国家级法国设计规划拉萨尽快赶回监考老师的风格是看来撒骨灰两个据类”); // 省略后面所有字段的设置,都设置一样的字段 …… return deepCopyEntity; } }

总结:

1)、格式化操控性 Clone > new > Kryo格式化 > Jdk格式化 > Json(各种Json类似于)格式化

2)、Clone深复本操控性最低,但假如特性中有特定的第一类字段,则须要自己撰写标识符

3)、new 操控性仅次于Clone,因为须要执行Jvm操作过程(自变量池推论,缓存再次分配,值初始化,init形式初始化,栈中第一类的提及等),

因此主要是每一第一类须要单独撰写标识符,当然也不建议采用反射

4)、kryo 操控性较高,因此不须要单独的开发, 若对操控性不是特别高,能考虑采用.

kryo亦然线程安全的,工程项目中采用时能放入ThreadLocal中

5)、Jdk格式化和Json格式化,操控性太低,高操控性工程项目不建议采用

假如操控性要求特别高(或者第一类结构层次不深),能采用Clone形式;

否则能考虑采用 Kryo格式化和反格式化同时实现第一类深复本

结尾

本文到这里就结束了,感谢看到最后的朋友,都看到最后了,点个赞再走啊,如有不对之处还请多多指正。

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务