JAVA -- オブジェクトのディープコピー

Cloneable インターフェイスを継承し、clone メソッドを再実装します。

エンティティ クラスは、Cloneableインターフェイスを継承し、clone()メソッドを再実装する必要があります。
エンティティ クラスで参照されるポインター型要素も Cloneable インターフェイスを継承し、 clone() メソッドを再実装するか、新しいオブジェクトを作成して要素に割り当てることに注意してください。

@Data
public class CloneableEntity implements Cloneable{
    
    
    private String stringValue;
    private PointerEntity pointerEntity;
    private List<String> listValue=  new ArrayList<>();

    //填充列表 用来比较克隆效率
    public void initListValue() {
    
    
        for (int i = 0; i < 10000; i++) {
    
    
            this.listValue.add(UUID.randomUUID().toString());
        }
    }

    @Override
    public CloneableEntity clone() throws CloneNotSupportedException {
    
    
        CloneableEntity clone = (CloneableEntity)super.clone();
		// 指针类型的元素要做clone一份新的
		if(null!=this.pointerEntity) {
    
    
        	clone.setPointerEntity(this.pointerEntity.clone());
        }
        // 创建一个新的对象给元素
        clone.setListValue(new ArrayList<>(listValue));
        return clone;
    }
}
@Data
public class PointerEntity implements Cloneable{
    
    
    private String uuid;

    public PointerEntity(){
    
    
    }

    public PointerEntity(String uuid){
    
    
        this.uuid = uuid;
    }

    @Override
    protected PointerEntity clone() throws CloneNotSupportedException {
    
    
        return (PointerEntity)super.clone();
    }
}

クローンインスタンス

クローン作成された新しいオブジェクトの要素を変更しても、元のオブジェクトの要素の値には影響しません。

Gson gson = new Gson();//用来打印对象
//        Cloneable 深拷贝
CloneableEntity entity1 = new CloneableEntity();
entity1.setStringValue("1");
entity1.setPointerEntity(new PointerEntity(UUID.randomUUID().toString()));
entity1.setListValue(new ArrayList<String>(){
    
    {
    
    add("1");add("2");}});
CloneableEntity clone1 = entity1.clone();
//对克隆后的对象进行修改
clone1.setStringValue("1--1");
clone1.getListValue().add("3");
clone1.getPointerEntity().setUuid(UUID.randomUUID().toString());
System.out.println("entity1: "+gson.toJson(entity1));
System.out.println("clone1: "+gson.toJson(clone1));
-------------------------------
entity1: {
    
    "stringValue":"1","pointerEntity":{
    
    "uuid":"27bb47c6-1ad3-4b79-a408-509f2163b5fa"},"listValue":["1","2"]}
clone1: {
    
    "stringValue":"1--1","pointerEntity":{
    
    "uuid":"a3495d1d-e32c-4dee-be57-0a8a3f30678c"},"listValue":["1","2","3"]}

ストリームを使用したシリアル化と逆シリアル化

クローンされたクラスはシリアル化をサポートする必要があり、クラス内の要素もシリアル化をサポートする必要があります。そうしないと、クローン作成中にシリアル化をサポートしない例外がポップアップ表示されます。

@Data
public class Serialzable2Entity implements Serializable {
    
    
    private String stringValue;
    private SerialzablePointerEntity pointerEntity;
    private List<String> listValue=  new ArrayList<>();

    //填充列表 用来比较克隆效率
    public void initListValue() {
    
    
        for (int i = 0; i < 10000; i++) {
    
    
            this.listValue.add(UUID.randomUUID().toString());
        }
    }
}
@Data
public class SerialzablePointerEntity implements Serializable {
    
    
    private String uuid;
    public SerialzablePointerEntity(){
    
    
    }

    public SerialzablePointerEntity(String uuid){
    
    
        this.uuid = uuid;
    }

}

クローンインスタンス

Gson gson = new Gson();//用来打印对象
//流深拷贝
Serialzable2Entity entity4 = new Serialzable2Entity();
entity4.setStringValue("41");
entity4.setPointerEntity(new SerialzablePointerEntity(UUID.randomUUID().toString()));
entity4.setListValue(new ArrayList<String>(){
    
    {
    
    add("41");add("42");}});
Serialzable2Entity clone4 ;
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
  ObjectOutputStream oos = new ObjectOutputStream(bos);
) {
    
    
 oos.writeObject(entity4);
 oos.flush();
 try (
      ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
      ObjectInputStream ois = new ObjectInputStream(bis);
 ) {
    
    
     clone4 = (Serialzable2Entity) ois.readObject();
 }
}
clone4.setStringValue("41--1");
clone4.getListValue().add("43");
clone4.getPointerEntity().setUuid(UUID.randomUUID().toString());
System.out.println("entity4: " + gson.toJson(entity4));
System.out.println("clone4: " + gson.toJson(clone4));
---------------------------------------
entity4: {
    
    "stringValue":"41","pointerEntity":{
    
    "uuid":"af3c287f-1289-4fb3-947f-f5b7684ef07e"},"listValue":["41","42"]}
clone4: {
    
    "stringValue":"41--1","pointerEntity":{
    
    "uuid":"0549e071-d7df-469f-baae-578e9aa89194"},"listValue":["41","42","43"]}

Apache Commons Lang シリアライザーの使用

Apache Commons Langにはシリアル化ツール クラスSerializationUtilsが付属しており、このクラスではclone()メソッドを使用してオブジェクトのディープ コピーを作成できます。
実際、その原理は、より多くの型変換とその他の操作があることを除いて、上記のストリームのシリアル化および逆シリアル化と同じです。したがって、
複製されたクラスがシリアル化をサポートする必要があり、クラス内の要素もシリアル化をサポートする必要があります。そうしないと、クローン作成中にシリアル化をサポートしない例外がポップアップ表示されます。

クローンインスタンス

Gson gson = new Gson();//用来打印对象
// Apache Commons Lang序列化深拷贝 拷贝对象和对象中所有涉及到的指针类型的对象都要实现序列化
Serialzable2Entity entity2 = new Serialzable2Entity();
entity2.setStringValue("11");
entity2.setPointerEntity(new SerialzablePointerEntity(UUID.randomUUID().toString()));
entity2.setListValue(new ArrayList<String>(){
    
    {
    
    add("11");add("12");}});
Serialzable2Entity clone2 = SerializationUtils.clone(entity2);
//对克隆后的对象进行修改
clone2.setStringValue("11--1");
clone2.getListValue().add("13--1");
clone2.getPointerEntity().setUuid(UUID.randomUUID().toString());
System.out.println("entity2: "+gson.toJson(entity2));
System.out.println("clone2: "+gson.toJson(clone2));
------------------------------------------
entity2: {
    
    "stringValue":"11","pointerEntity":{
    
    "uuid":"303e0bff-ad81-4b56-a1db-311cf894b189"},"listValue":["11","12"]}
clone2: {
    
    "stringValue":"11--1","pointerEntity":{
    
    "uuid":"bb42fd5b-d0ce-483a-9f34-d32e1b0eab5c"},"listValue":["11","12","13--1"]}

Gson を使用してシリアル化と逆シリアル化を行う

Gson のシリアル化と逆シリアル化を使用してクローンを作成します。クローンされたオブジェクトには拡張機能は必要ありません。

@Data
public class NormalEntity{
    
    
    private String stringValue;
    private NormalPointerEntity pointerEntity;
    private List<String> listValue=  new ArrayList<>();

    /**
     * 初始化列表
     */
    public void initListValue() {
    
    
        for (int i = 0; i < 10000; i++) {
    
    
            this.listValue.add(UUID.randomUUID().toString());
        }
    }
}

@Data
public class NormalPointerEntity{
    
    
    private String uuid;

    public NormalPointerEntity(){
    
    
    }

    public NormalPointerEntity(String uuid){
    
    
        this.uuid = uuid;
    }
}
Gson gson = new Gson();
// Gson 序列化反序列化深拷贝 对对象没有要求
NormalEntity entity3 = new NormalEntity();
entity3.setStringValue("21");
entity3.setPointerEntity(new NormalPointerEntity(UUID.randomUUID().toString()));
entity3.setListValue(new ArrayList<String>(){
    
    {
    
    add("21");add("22");}});
NormalEntity clone3 = gson.fromJson(gson.toJson(entity3 ), NormalEntity.class);
//修改克隆后的对象
clone3.setStringValue("21--1");
clone3.getListValue().add("23");
clone3.getPointerEntity().setUuid(UUID.randomUUID().toString());
System.out.println("entity3: "+gson.toJson(entity3));
System.out.println("clone3: "+gson.toJson(clone3));
---------------------------
entity3: {
    
    "stringValue":"21","pointerEntity":{
    
    "uuid":"b81d7ee9-54db-4d51-b09d-0a61adce4518"},"listValue":["21","22"]}
clone3: {
    
    "stringValue":"21--1","pointerEntity":{
    
    "uuid":"276de4f8-0b23-4435-9225-dfc836311064"},"listValue":["21","22","23"]}

長所と短所を比較する

まずは効率を比較してみます


StopWatch stopWatch = new StopWatch();
//        Cloneable 深拷贝
CloneableEntity entity1 = new CloneableEntity();
entity1.initListValue();
stopWatch.start("Cloneable 深拷贝");
CloneableEntity clone1 = entity1.clone();
stopWatch.stop();

//        流深拷贝
Serialzable2Entity entity4 = new Serialzable2Entity();
entity4.initListValue();
Serialzable2Entity clone4 ;
stopWatch.start(" 流深拷贝");
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
     ObjectOutputStream oos = new ObjectOutputStream(bos);
) {
    
    
    oos.writeObject(entity4);
    oos.flush();
    try (
            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
    ) {
    
    
        clone4 = (Serialzable2Entity) ois.readObject();
    }
}
stopWatch.stop();

//        Apache Commons Lang序列化深拷贝 拷贝对象和对象中所有涉及到的指针类型的对象都要实现序列化
Serialzable2Entity entity2 = new Serialzable2Entity();
entity2.initListValue();
stopWatch.start("Apache Commons Lang序列化深拷贝");
Serialzable2Entity clone2 = SerializationUtils.clone(entity2);
stopWatch.stop();

//        Gson 序列化反序列化深拷贝 对对象没有要求
NormalEntity entity3 = new NormalEntity();
entity3.initListValue();
stopWatch.start(" Gson 序列化反序列化深拷贝");
Gson gson = new Gson();//用来打印对象
NormalEntity clone3 = gson.fromJson(gson.toJson(entity3 ), NormalEntity.class);
stopWatch.stop();

System.out.println(stopWatch.prettyPrint());
-----------------------------------
---------------------------------------------
ns         %     Task name
---------------------------------------------
000029100  000%  Cloneable 深拷贝
048922700  023%  流深拷贝
017047500  008%  Apache Commons Lang序列化深拷贝
150727000  070%  Gson 序列化反序列化深拷贝

消費時間によると、 Cloneableインターフェイスの継承によるクローン作成が最も効率的で、次にストリーム シリアル化とSerializationUtilsが続き、Gson シリアル化が最も遅いことがわかります。ストリーミング ディープ コピーとSerializationUtils
の効率は実際には同様であることに注意してください。基本的にすべてのストリームがシリアル化と逆シリアル化に使用されるためです。ただし、プログラム起動後の最初のIOクラスのロード時に一部時間が消費されるため、SerializationUtilsクローニングを実行した場合、ストリームシリアル化よりも消費時間は短くなります。ストリームのシリアル化とSerializationUtilsの実行順序を入れ替えると、ストリームのシリアル化の方がSerializationUtilsよりも所要時間が短いことがわかります

メリットとデメリットまとめ

方法 アドバンテージ 不利益
クローン可能インターフェースを継承する 効率が高く、他の jar パッケージを参照する必要がなく、システムのオーバーヘッドが低い クローンされたクラスは Cloneable インターフェイスを書き換える必要があり、クラス内のポインター要素を手動でクローンすることを選択する必要があります。また、クラス内のフィールドが更新されたときに、ディープ コピーの失敗を防ぐために clone メソッドも適時に更新する必要があります。
ストリームのシリアル化 他の jar パッケージを参照する必要はなく、フィールドを追加および変更するためにコードを調整する必要もありません。 効率は平均的ですが、クラスはシリアル化をサポートする必要があり、シリアル化と逆シリアル化のプロセスには大きなシステム オーバーヘッドがあります。
SerializationUtils.clone() コードは簡潔で使いやすく、フィールドの追加や変更にコードを調整する必要はありません。 効率は平均的で、Apache 共通 jar パッケージを導入する必要があり、クラスはシリアル化をサポートする必要があり、シリアル化と逆シリアル化のプロセスには大きなシステム オーバーヘッドがあります。
Gson のシリアル化 クラスの前処理は必要ありません 効率は非常に低く、jar パッケージを導入する必要があり、シリアル化と逆シリアル化のプロセスには大きなシステム オーバーヘッドがかかります。

おすすめ

転載: blog.csdn.net/qq_40096897/article/details/130700014