339

オブジェクトの深いコピー機能を実装するのは少し難しいです。元のオブジェクトと複製されたオブジェクトが参照を共有しないようにするために、どのような手順を実行しますか?

4

20 に答える 20

182

安全な方法は、オブジェクトをシリアル化してから逆シリアル化することです。これにより、すべてが真新しいリファレンスになります。

これを効率的に行う方法については、次の記事を参照してください。

警告:シングルトンなどの新しいインスタンスが作成されないように、クラスがシリアライゼーションをオーバーライドする可能性があります。また、クラスがシリアライズ可能でない場合、これはもちろん機能しません。

于 2008-09-15T15:42:23.633 に答える
81

一部の人々は、使用またはオーバーライドについて言及していますObject.clone()。やらないでください。Object.clone()にはいくつかの重大な問題があり、ほとんどの場合、その使用はお勧めできません。完全な回答については、Joshua Bloch による「 Effective Java 」の項目 11 を参照してください。プリミティブ型の配列で安全に使用できると思いますがObject.clone()、それとは別に、クローンを適切に使用してオーバーライドすることについて慎重に考える必要があります。

シリアライゼーション (XML など) に依存するスキームは、扱いにくいものです。

ここには簡単な答えはありません。オブジェクトをディープ コピーする場合は、オブジェクト グラフを走査し、オブジェクトのコピー コンストラクターまたは子オブジェクトをディープ コピーする静的ファクトリ メソッドを介して、各子オブジェクトを明示的にコピーする必要があります。不変 (例: Strings) はコピーする必要はありません。余談ですが、この理由から、不変性を優先する必要があります。

于 2008-12-09T22:38:22.177 に答える
62

ファイルを作成せずにシリアル化でディープ コピーを作成できます。

ディープ コピーするオブジェクトは、implement serializable. クラスが final でないか、変更できない場合は、クラスを拡張して serializable を実装します。

クラスをバイト ストリームに変換します。

ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(object);
oos.flush();
oos.close();
bos.close();
byte[] byteData = bos.toByteArray();

バイト ストリームからクラスを復元​​します。

ByteArrayInputStream bais = new ByteArrayInputStream(byteData);
Object object = new ObjectInputStream(bais).readObject();
于 2011-09-29T11:17:53.417 に答える
45

Apache Commons Langを使用してシリアライゼーション ベースのディープ クローンを実行できますが、org.apache.commons.lang3.SerializationUtils.clone(T)注意してください。パフォーマンスは最悪です。

一般に、複製が必要なオブジェクト グラフ内のオブジェクトのクラスごとに、独自の複製メソッドを作成することをお勧めします。

于 2008-09-15T16:48:58.060 に答える
29

ディープ コピーを実装する 1 つの方法は、関連する各クラスにコピー コンストラクターを追加することです。コピー コンストラクターは、'this' のインスタンスを 1 つの引数として取り、そこからすべての値をコピーします。かなりの作業がありますが、かなり簡単で安全です。

編集:フィールドを読み取るためにアクセサメソッドを使用する必要がないことに注意してください。ソース インスタンスは常にコピー コンストラクターを持つインスタンスと同じ型であるため、すべてのフィールドに直接アクセスできます。当たり前だけど見落としがち。

例:

public class Order {

    private long number;

    public Order() {
    }

    /**
     * Copy constructor
     */
    public Order(Order source) {
        number = source.number;
    }
}


public class Customer {

    private String name;
    private List<Order> orders = new ArrayList<Order>();

    public Customer() {
    }

    /**
     * Copy constructor
     */
    public Customer(Customer source) {
        name = source.name;
        for (Order sourceOrder : source.orders) {
            orders.add(new Order(sourceOrder));
        }
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

編集: コピー コンストラクターは継承を考慮しないことに注意してください。例: OnlineOrder (Order のサブクラス) をコピー コンストラクターに渡すと、明示的に解決しない限り、通常の Order インスタンスがコピー内に作成されます。リフレクションを使用して、引数の実行時型でコピー コンストラクターを検索できます。ただし、継承を一般的な方法でカバーする必要がある場合は、このルートに行かずに別の解決策を探すことをお勧めします。

于 2011-09-29T11:57:14.180 に答える
22

シンプルな API を持つライブラリを使用でき、リフレクションを使用して比較的高速にクローン作成を実行できます (シリアル化メソッドよりも高速である必要があります)。

Cloner cloner = new Cloner();

MyClass clone = cloner.deepClone(o);
// clone is a deep-clone of o
于 2014-03-20T22:52:09.387 に答える
12

複雑なオブジェクトの場合、パフォーマンスが重要でない場合は、gsonなどの json ライブラリを使用 してオブジェクトを json テキストにシリアル化し、テキストを逆シリアル化して新しいオブジェクトを取得します。

リフレクションに基づく gson は、ほとんどの場合に機能しますが、transientフィールドがコピーされず、オブジェクトが原因で循環参照されることを除きますStackOverflowError

public static <T> T copy(T anObject, Class<T> classInfo) {
    Gson gson = new GsonBuilder().create();
    String text = gson.toJson(anObject);
    T newObject = gson.fromJson(text, classInfo);
    return newObject;
}
public static void main(String[] args) {
    String originalObject = "hello";
    String copiedObject = copy(originalObject, String.class);
}
于 2016-11-29T07:24:14.260 に答える
11

XStream は、そのような場合に非常に役立ちます。これはクローン作成を行うための簡単なコードです

private static final XStream XSTREAM = new XStream();
...

Object newObject = XSTREAM.fromXML(XSTREAM.toXML(obj));
于 2008-10-23T08:03:08.533 に答える
10

非常に簡単でシンプルなアプローチの1つは、Jackson JSONを使用して複雑なJavaオブジェクトをJSONにシリアル化し、それを読み戻すことです。

https://github.com/FasterXML/jackson-databind/#5-minute-tutorial-streaming-parser-generatorから:

JsonFactory f = mapper.getFactory(); // may alternatively construct directly too

// First: write simple JSON output
File jsonFile = new File("test.json");
JsonGenerator g = f.createGenerator(jsonFile);
// write JSON: { "message" : "Hello world!" }
g.writeStartObject();
g.writeStringField("message", "Hello world!");
g.writeEndObject();
g.close();

// Second: read file back
JsonParser p = f.createParser(jsonFile);

JsonToken t = p.nextToken(); // Should be JsonToken.START_OBJECT
t = p.nextToken(); // JsonToken.FIELD_NAME
if ((t != JsonToken.FIELD_NAME) || !"message".equals(p.getCurrentName())) {
   // handle error
}
t = p.nextToken();
if (t != JsonToken.VALUE_STRING) {
   // similarly
}
String msg = p.getText();
System.out.printf("My message to you is: %s!\n", msg);
p.close();
于 2012-02-07T03:51:31.897 に答える
8

XStream( http://x-stream.github.io/ ) を使用します。注釈を使用するか、プロパティ名を XStream クラスに明示的に指定することで、どのプロパティを無視できるかを制御することもできます。さらに、クローン可能なインターフェースを実装する必要はありません。

于 2008-09-16T05:08:35.693 に答える
7

ディープ コピーは、各クラスの同意がある場合にのみ実行できます。クラス階層を制御できる場合は、複製可能なインターフェイスを実装し、Clone メソッドを実装できます。そうしないと、オブジェクトがデータ以外のリソース (データベース接続など) を共有している可能性があるため、ディープ コピーを安全に実行することは不可能です。ただし、一般に、ディープ コピーは Java 環境では不適切な方法と見なされており、適切な設計方法に従って回避する必要があります。

于 2008-09-15T15:44:32.333 に答える
6
import com.thoughtworks.xstream.XStream;

public class deepCopy {
    private static  XStream xstream = new XStream();

    //serialize with Xstream them deserialize ...
    public static Object deepCopy(Object obj){
        return xstream.fromXML(xstream.toXML(obj));
    }
}
于 2011-09-15T13:55:43.983 に答える
5

Java オブジェクトのクローン作成に Dozer を使用しましたが、その点で優れています。Kryoライブラリは別の優れた代替手段です。

于 2014-10-17T02:08:59.940 に答える
2

1)

public static Object deepClone(Object object) {
   try {
     ByteArrayOutputStream baos = new ByteArrayOutputStream();
     ObjectOutputStream oos = new ObjectOutputStream(baos);
     oos.writeObject(object);
     ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
     ObjectInputStream ois = new ObjectInputStream(bais);
     return ois.readObject();
   }
   catch (Exception e) {
     e.printStackTrace();
     return null;
   }
 }

2)

    // (1) create a MyPerson object named Al
    MyAddress address = new MyAddress("Vishrantwadi ", "Pune", "India");
    MyPerson al = new MyPerson("Al", "Arun", address);

    // (2) make a deep clone of Al
    MyPerson neighbor = (MyPerson)deepClone(al);

ここで、 MyPerson および MyAddress クラスはシリアライズ可能なインターフェースを実装する必要があります

于 2016-11-11T14:51:53.347 に答える
1

以下は、バイト配列ストリームを使用したオブジェクトのシリアル化と逆シリアル化を使用した一般的なディープ クローニング メソッドです (ファイルへの書き込みを回避するため)。

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

@SuppressWarnings("unchecked")
public static <T extends Serializable> T deepClone(T t) {
    try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);) {
        oos.writeObject(t);
        byte[] bytes = baos.toByteArray();
        try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes))) {
            return (T) ois.readObject();
        }
    } catch (IOException | ClassNotFoundException e) {
        throw new RuntimeException(e);
    }
}
于 2021-01-05T18:58:52.357 に答える