欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-05-26 15:20:08
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的序列化操作,不建议用此种方案进行深度拷贝。

参考文献

https://github.com/esotericsoftware/kryo

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