web-dev-qa-db-ja.com

ディープクローンユーティリティの推奨事項

Javaコレクションのディープクローニング用のユーティリティはありますか?

  • 配列
  • リスト
  • 地図

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

71
Juraj

以前の緑の答えは悪かったと思います、なぜあなたは尋ねるかもしれませんか?

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

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

だから解決策は何ですか:

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

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

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

63
Cojones

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

クローン

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

Clone()のその他の問題については、Joshua Blochの本の項目11を参照してください " Effective Java、Second Edition "

シリアル化

シリアル化はさらに悪いです。 clone()の多くの欠陥があり、次にいくつかあります。ジョシュアには、このトピックだけの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タイプ(コレクションなど)の場合、これらをコピーできるユーティリティクラスを使用します。メソッドにはフラグとコールバックがあるため、コピーの深さを制御できます。

19
Aaron Digulla

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

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

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

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

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

    cloner.deepClone(anyObject);

16
Brad Cupit

私は、ブラッドが提示したクローンライブラリの作成者です。これは、余分なコードを記述することなくオブジェクトを複製するためのソリューションです(シリアル化可能なオブジェクトやimpl clone()メソッドは不要です)

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

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

14

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

Apache Commonsシリアル化ユーティリティクラス へのリンクについては、Brunoの回答を確認してください。これがあなたが取ることにしたルートである場合に役立ちます。

10
John Feminella

1つの可能性は、serializationを使用することです:

Apache Commonsは SerializationUtils を提供します

5
bruno conde

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

それを味わうために、クラスの例を以下に示します。

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

必要に応じて、休止状態固有の拡張機能もあります。

2

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

0
Marko