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的序列化操作,不建議用此種方案進行深度拷貝。
參考文獻
http://blog.csdn.net/isea533/article/details/9375907
https://github.com/EsotericSoftware/kryo
鏡像地址
http://www.zhangwei.wiki/#/posts/1