• Java 拷贝各种实现方式对比(浅拷贝、深拷贝)


    所有的测试代码在最下方。

    1. 实现Cloneable接口

    实现cloneable接口,重写clone类。

    直接使用object的clone方法,还是浅拷贝,需要自己重写clone方法,注意拷贝的对象中还包含其他对象的话,包含的对象也需要重写clone方法。

    这种方法效率最高,但是不好维护

    /**
     * @Description:
     * @Author: zhaoyuan
     * @Date: 2021/4/14 0014
     * @Time 14:45
     */
    public class Parent implements Cloneable{
        Integer id;
    
        String name;
    
        Child child;
        
        List<List<Child>> childrenList;
    
        List<Child> children;
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Child getChild() {
            return child;
        }
    
        public void setChild(Child child) {
            this.child = child;
        }
    
        public List<List<Child>> getChildrenList() {
            return childrenList;
        }
    
        public void setChildrenList(List<List<Child>> childrenList) {
            this.childrenList = childrenList;
        }
    
        public List<Child> getChildren() {
            return children;
        }
    
        public void setChildren(List<Child> children) {
            this.children = children;
        }
    
        @Override
        protected Parent clone() throws CloneNotSupportedException {
            Parent cloneParent = (Parent)super.clone();
            if (Objects.nonNull(this.child)) {
                cloneParent.setChild(this.child.clone());
            }
            if (!CollectionUtils.isEmpty(this.children)) {
                List<Child> cloneChildren = new ArrayList<>();
                for (Child child : this.children) {
                    cloneChildren.add(child.clone());
                }
                cloneParent.setChildren(cloneChildren);
            }
            if (!CollectionUtils.isEmpty(this.childrenList)) {
                List<List<Child>> cloneChildrenList = new ArrayList<>();
                for (List<Child> childList : this.childrenList) {
                    List<Child> cloneChild = new ArrayList<>();
                    for (Child child : childList) {
                        cloneChild.add(child.clone());
                    }
                    cloneChildrenList.add(cloneChild);
                }
                cloneParent.setChildrenList(cloneChildrenList);
            }
            
            return cloneParent;
        }
    }
    
    
    /**
     * @Description:
     * @Author: zhaoyuan
     * @Date: 2021/4/14 0014
     * @Time 14:48
     */
    public class Child implements Cloneable{
        Integer id;
    
        String name;
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        protected Child clone() throws CloneNotSupportedException {
            return (Child)super.clone();
        }
    }
    
    

    image-20210414154352632

    2. BeanUtils 是浅clone

    image-20210414154442550

    3. 使用Orika是深clone,但是多层嵌套数组,clone有问题,谨慎使用

    image-20210414154608123

    4. 序列化Clone,深clone,但是效率不高

    image-20210414154646360

    5. 转json完成clone,深clone

    我这里使用的hutool的json工具类转的,效率还没有序列化的方式高,但是其他人测试,Gson转比序列化效率高,这里不再测试,都没有自己重写clone效率高

    image-20210414154756660

    6. mapStrcuct

    还有使用mapStruct实现clone的,这里不过多解释,对象中有list存储对象的,也不建议使用,下面是mapStrcut生成的代码,list类型的,它是通过new ArrayList()的方式,是浅拷贝

    image-20210414155150236

    7. 所有测试代码

    package com.example.demo.domain.deepClone;
    
    import cn.hutool.json.JSONObject;
    import cn.hutool.json.JSONUtil;
    import com.example.demo.util.OrikaMapperUtils;
    import org.apache.commons.lang3.SerializationUtils;
    import org.junit.Assert;
    import org.junit.Before;
    import org.junit.Test;
    import org.springframework.beans.BeanUtils;
    import org.springframework.util.StopWatch;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @Description:
     * @Author: zhaoyuan
     * @Date: 2021/4/14 0014
     * @Time 15:00
     */
    public class TestDeepClone {
    
        public Parent parent;
        public StopWatch watch;
    
        @Before
        public void before() {
            parent = new Parent();
            parent.setId(1);
            parent.setName("parentName1");
            parent.setChild(new Child(1,"childName1"));
    
            Child child2 = new Child(2,"childName2");
            Child child3 = new Child(2,"childName3");
            Child child4 = new Child(2,"childName4");
            List<Child> children = new ArrayList<>();
            children.add(child2);
            children.add(child3);
            children.add(child4);
            parent.setChildren(children);
    
            List<List<Child>> childrenList = new ArrayList<>();
            Child child5 = new Child(2,"childName5");
            Child child6 = new Child(2,"childName6");
            Child child7 = new Child(2,"childName7");
            List<Child> children2 = new ArrayList<>();
            List<Child> children3 = new ArrayList<>();
            children2.add(child5);
            children2.add(child6);
            children3.add(child7);
            childrenList.add(children2);
            childrenList.add(children3);
            parent.setChildrenList(childrenList);
    
            watch = new StopWatch("clone");
        }
    
        @Test
        public void testClone() throws CloneNotSupportedException {
            watch.start();
            Parent clone = parent.clone();
            watch.stop();
            System.out.println(watch.prettyPrint());
            System.out.println(parent);
            System.out.println(clone);
            Assert.assertNotEquals(clone,parent);
            Assert.assertNotEquals(clone.getChild(),parent.getChild());
            Assert.assertNotEquals(clone.getChildren(),parent.getChildren());
            Assert.assertNotEquals(clone.getChildrenList(),parent.getChildrenList());
        }
    
        @Test
        public void testBeanUtilCopy(){
            watch.start();
            Parent clone = new Parent();
            BeanUtils.copyProperties(parent,clone);
            watch.stop();
            System.out.println(watch.prettyPrint());
    
            System.out.println(parent);
            System.out.println(clone);
            Assert.assertNotEquals(clone,parent);
            Assert.assertNotEquals(clone.getChild(),parent.getChild());
            Assert.assertNotEquals(clone.getChildren(),parent.getChildren());
            Assert.assertNotEquals(clone.getChildrenList(),parent.getChildrenList());
        }
    
        @Test
        public void testOrikaClone(){
            watch.start();
            Parent clone = OrikaMapperUtils.map(parent,Parent.class);
            watch.stop();
            System.out.println(watch.prettyPrint());
    
            System.out.println(parent);
            System.out.println(clone);
            Assert.assertNotEquals(clone,parent);
            Assert.assertNotEquals(clone.getChild(),parent.getChild());
            Assert.assertNotEquals(clone.getChildren(),parent.getChildren());
            Assert.assertNotEquals(clone.getChildrenList(),parent.getChildrenList());
        }
    
         /**
          * 需要类实现 Serializable
         */
          @Test
          public void testSerializationClone()  {
              watch.start();
              Parent clone = SerializationUtils.clone(parent);
              watch.stop();
              System.out.println(watch.prettyPrint());
            
              System.out.println(parent);
              System.out.println(clone);
              Assert.assertNotEquals(clone,parent);
              Assert.assertNotEquals(clone.getChild(),parent.getChild());
              Assert.assertNotEquals(clone.getChildren(),parent.getChildren());
              Assert.assertNotEquals(clone.getChildrenList(),parent.getChildrenList());
          }
    
      @Test
        public  void testJsonClone(){
          watch.start();
          JSONObject parse = JSONUtil.parseObj(parent);
          Parent clone = JSONUtil.toBean(parse, Parent.class);
          watch.stop();
          System.out.println(watch.prettyPrint());
    
          System.out.println(parent);
          System.out.println(clone);
          Assert.assertNotEquals(clone,parent);
          Assert.assertNotEquals(clone.getChild(),parent.getChild());
          Assert.assertNotEquals(clone.getChildren(),parent.getChildren());
          Assert.assertNotEquals(clone.getChildrenList(),parent.getChildrenList());
      }
    
    }
    
    

    8. 效率总结

    数据来源:https://zhuanlan.zhihu.com/p/260117694

    没有测试kryo序列化,看起来效率也还可以

     *  深度拷贝类型        循环次数[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序列化和反序列化实现对象深拷贝==
  • 相关阅读:
    最新免费网络加速器,游戏加速器下载_网络加速器永久免费版哪个好?
    HTML中特殊符号编码对照表,html特殊符号编码都有哪些?
    超融合架构在汽车行业前景及未来发展
    好用的前端开发小工具
    layui省市区三级联动城市选择
    企业超融合解决方案怎么做?超融合基础架构 超融合优势是什么?
    最新免费网络加速器
    什么是超融合?
    面临网络安全危机时需要思考的20个问题
    2019年超融合将飞速发展 三大趋势不容错过
  • 原文地址:https://www.cnblogs.com/zhaoyuan72/p/14658341.html
Copyright © 2020-2023  润新知