76

Javaコレクションのディープクローン作成のためのユーティリティはありますか?

  • 配列
  • リスト
  • マップ

注:シリアル化を使用せずに、Object.clone()メソッドを使用するソリューションをお勧めします。私のカスタムオブジェクトはclone()メソッドを実装し、クローン可能なjava標準クラスのみを使用することを確信できます...

4

8 に答える 8

65

以前の緑色の回答は悪かったと思いますが、なぜ質問するのでしょうか?

  • 多くのコードを追加します
  • コピーするすべてのフィールドをリストしてこれを行う必要があります
  • clone() を使用する場合、これはリストでは機能しません (これは HashMap の clone() が言うことです: この HashMap インスタンスの浅いコピーを返します: キーと値自体は複製されません.) したがって、手動で行うことになります (これにより、私は泣く)

ああ、ちなみに、シリアル化も悪いです。Serializable をあちこちに追加する必要があるかもしれません (これも泣けます)。

それで、解決策は何ですか:

Java Deep-Cloning ライブラリ クローン ライブラリは、オブジェクトをディープ クローンする小さなオープン ソース (Apache ライセンス) Java ライブラリです。オブジェクトは Cloneable インターフェースを実装する必要はありません。事実上、このライブラリは任意の Java オブジェクトを複製できます。キャッシュされたオブジェクトを変更したくない場合、またはオブジェクトのディープコピーを作成したい場合はいつでも、キャッシュの実装で使用できます。

Cloner cloner=new Cloner();
XX clone = cloner.deepClone(someObjectOfTypeXX);

https://github.com/kostaskougios/cloningで確認してください。

于 2009-08-06T20:07:50.177 に答える
20

Javaでオブジェクトをコピーするすべてのアプローチには、重大な欠陥があります。

クローン

  1. clone()メソッドは保護されているため、問題のクラスがパブリックメソッドでオーバーライドしない限り、直接呼び出すことはできません。
  2. clone()はコンストラクターを呼び出しません。任意のコンストラクター。メモリを割り当て、内部classフィールド(を介して読み取ることができますgetClass())を割り当て、元のフィールドをコピーします。

clone()のその他の問題については、JoshuaBlochの著書「EffectiveJava、SecondEdition」の項目11を参照してください。

シリアライズ

シリアル化はさらに悪いです。それには多くの欠陥がclone()あり、それからいくつかの欠陥があります。Joshuaには、このトピックだけで4つの項目を含む章全体があります。

私の解決策

私の解決策は、プロジェクトに新しいインターフェイスを追加することです。

public interface Copyable<T> {
    T copy ();
    T createForCopy ();
    void copyTo (T dest);
}

コードは次のようになります。

class Demo implements Copyable<Demo> {
    public Demo copy () {
        Demo copy = createForCopy ();
        copyTo (copy);
        return copy;
    }
    public Demo createForCopy () {
        return new Demo ();
    }
    public void copyTo (Demo dest)
        super.copyTo (dest);
        ...copy fields of Demo here...
    }
}

残念ながら、このコードをすべてのオブジェクトにコピーする必要がありますが、常に同じコードであるため、Eclipseエディターテンプレートを使用できます。利点:

  1. どのコンストラクターを呼び出すか、どのフィールドを初期化するかを決めることができます。
  2. 初期化は決定論的な順序で行われます(ルートクラスからインスタンスクラスへ)
  3. 既存のオブジェクトを再利用して上書きできます
  4. タイプセーフ
  5. シングルトンはシングルトンのままです

標準のJavaタイプ(コレクションなど)の場合、それらをコピーできるユーティリティクラスを使用します。メソッドにはフラグとコールバックがあるので、コピーの深さを制御できます。

于 2009-03-20T13:52:16.887 に答える
17

コレクションの浅いクローン作成は簡単ですが、ディープクローンを作成する場合は、ライブラリを手動でコーディングするよりも優れている可能性があります(コレクションの要素もクローンする必要があるため)。

この回答と同じように、私はClonerライブラリを使用し、特にXStream(シリアル化してから逆シリアル化することで「クローン」できる)およびバイナリシリアル化に対してパフォーマンステストを行いました。XStreamはxmlとの間のシリアル化が非常に高速ですが、Clonerはクローン作成がはるかに高速です。

0.0851 ms:xstream(シリアル化/逆シリアル化によるクローン作成)
0.0223 ms:バイナリシリアル化(シリアル化/逆シリアル化によるクローン作成)
0.0017 ms:cloner
*単純なオブジェクト(2つのフィールド)のクローンを作成する平均時間。デフォルトのパブリックコンストラクターはありません。10,000回実行します。

高速であることに加えて、clonerを選択する理由は次のとおりです。

  1. 任意のオブジェクトのディープクローンを実行します(自分で作成していないオブジェクトも含む)
  2. フィールドを追加するたびにclone()メソッドを最新の状態に保つ必要はありません
  3. デフォルトのパブリックコンストラクターを持たないオブジェクトのクローンを作成できます
  4. Springで動作します
  5. (最適化)既知の不変オブジェクト(整数、文字列など)のクローンを作成しません
  6. 使いやすい。例:

    cloner.deepClone(anyObject);

于 2009-08-06T20:59:46.780 に答える
14

私は、Brad が提示したクローン ライブラリの作成者です。これは、余分なコードを書く必要のないオブジェクトのクローンを作成するためのソリューションです (シリアライズ可能なオブジェクトや impl clone() メソッドは必要ありません)。

Brad が言ったように非常に高速で、最近、さらに高速なバージョンをアップロードしました。clone() メソッドを手動で実装すると、Clone lib よりも高速になりますが、多くのコードを記述する必要があることに注意してください。

Cloner lib は、トラフィックが非常に多い (1 日あたり約 100 万リクエスト) サイトのキャッシュ実装で使用しているため、非常にうまく機能しています。キャッシュは、リクエストごとに約 10 個のオブジェクトを複製する必要があります。それは非常に信頼性が高く安定しています。ただし、クローン作成にはリスクがないわけではないことに注意してください。lib は、開発中に複製するすべてのクラス インスタンスを println するように構成できます。このようにして、クローンする必要があると思われるものをクローンするかどうかを確認できます。オブジェクト グラフは非常に深く、驚くほど大量のオブジェクトへの参照を含むことができます。clone lib を使用すると、不要なオブジェクト (シングルトンなど) を複製しないように指示できます。

于 2009-10-27T22:48:13.030 に答える
10

任意のコレクションをディープ クローンする一般的な方法の 1 つは、それをストリームにシリアライズしてから、新しいコレクションに読み戻すことです。同一のコピーである以外は、古いオブジェクトとは何の関係もない、完全に新しいオブジェクトを復元します。

Apache Commons serialization utility classesへのリンクについては、Bruno の回答を確認してください。

于 2009-03-20T12:09:29.477 に答える
5

1 つの可能性は、シリアライゼーションを使用することです。

Apache Commons はSerializationUtilsを提供します

于 2009-03-20T12:10:01.893 に答える
2

私はこの複製ライブラリを使用しましたが、非常に便利であることがわかりました。いくつかの制限があったため (どのフィールド、どのコンテキストで、どの程度の深さで複製する必要があるかなど、複製プロセスをより細かく制御する必要がありました)、拡張バージョンを作成しました。エンティティ クラスでフィールドに注釈を付けることで、フィールドの複製を制御します。

雰囲気をつかむために、クラスの例を次に示します。

public class CloneMePlease {
    @Clone(Skip.class)
    String id3 = UUID.randomUUID().toString();

    @Clone(Null.class)
    String id4 = UUID.randomUUID().toString();

    @Clone(value = RandomUUID.class, groups=CustomActivationGroup1.class)
    String id5 = UUID.randomUUID().toString();

    @Clone.List({
            @Clone(groups=CustomActivationGroup2.class, value=Skip.class),
            @Clone(groups=CustomActivationGroup3.class, value=Copy.class)})
    Object activationGroupOrderTest = new Object();

    @Clone(LongIncrement.class)
    long version = 1l;

    @PostClone
    private void postClone(CloneMePlease original, @CloneInject CloneInjectedService service){
         //do stuff with the original source object in the context of the cloned object
         //you can inject whatewer service you want, from spring/guice to perform custom logic here
    }
}

詳細はこちら: https://github.com/mnorbi/fluidity-cloning

必要な場合に備えて、休止状態固有の拡張機能もあります。

于 2013-04-09T19:28:01.040 に答える
0

シリアライゼーションとデシリアライゼーションを使用しますが、このアプローチは一時フィールドを持たないシリアライズ可能クラスでのみ機能することに注意してください。また、シングルトンはシングルトンではなくなります。

于 2009-03-20T12:11:32.793 に答える