• Java几种深度拷贝方法效率比较


    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的序列化操作,不建议用此种方案进行深度拷贝。

    参考文献

    http://blog.csdn.net/isea533/article/details/9375907

    https://github.com/EsotericSoftware/kryo

    镜像地址

    http://www.zhangwei.wiki/#/posts/1

    pay

  • 相关阅读:
    mysql之创建数据库,创建数据表
    mysql之group by,order by
    一个人选出2门以上不及格的课程sql语句
    GIt入门
    数据库索引工作原理
    题目:50个人围城一圈数到3和3的倍数时出圈,问剩下的人是谁?原来的位置是多少?
    约瑟夫环:递归算法
    K-means算法的java实现,聚类分析681个三国武将
    java用一个for循环输出99乘法表
    写一个基于UDP协议的聊天小程序
  • 原文地址:https://www.cnblogs.com/coderzhw/p/11094284.html
Copyright © 2020-2023  润新知