Javaコレクションのディープクローン作成のためのユーティリティはありますか?
- 配列
- リスト
- マップ
注:シリアル化を使用せずに、Object.clone()メソッドを使用するソリューションをお勧めします。私のカスタムオブジェクトはclone()メソッドを実装し、クローン可能なjava標準クラスのみを使用することを確信できます...
以前の緑色の回答は悪かったと思いますが、なぜ質問するのでしょうか?
ああ、ちなみに、シリアル化も悪いです。Serializable をあちこちに追加する必要があるかもしれません (これも泣けます)。
それで、解決策は何ですか:
Java Deep-Cloning ライブラリ クローン ライブラリは、オブジェクトをディープ クローンする小さなオープン ソース (Apache ライセンス) Java ライブラリです。オブジェクトは Cloneable インターフェースを実装する必要はありません。事実上、このライブラリは任意の Java オブジェクトを複製できます。キャッシュされたオブジェクトを変更したくない場合、またはオブジェクトのディープコピーを作成したい場合はいつでも、キャッシュの実装で使用できます。
Cloner cloner=new Cloner();
XX clone = cloner.deepClone(someObjectOfTypeXX);
https://github.com/kostaskougios/cloningで確認してください。
Javaでオブジェクトをコピーするすべてのアプローチには、重大な欠陥があります。
クローン
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エディターテンプレートを使用できます。利点:
標準のJavaタイプ(コレクションなど)の場合、それらをコピーできるユーティリティクラスを使用します。メソッドにはフラグとコールバックがあるので、コピーの深さを制御できます。
コレクションの浅いクローン作成は簡単ですが、ディープクローンを作成する場合は、ライブラリを手動でコーディングするよりも優れている可能性があります(コレクション内の要素もクローンする必要があるため)。
この回答と同じように、私はClonerライブラリを使用し、特にXStream(シリアル化してから逆シリアル化することで「クローン」できる)およびバイナリシリアル化に対してパフォーマンステストを行いました。XStreamはxmlとの間のシリアル化が非常に高速ですが、Clonerはクローン作成がはるかに高速です。
0.0851 ms:xstream(シリアル化/逆シリアル化によるクローン作成)
0.0223 ms:バイナリシリアル化(シリアル化/逆シリアル化によるクローン作成)
0.0017 ms:cloner
*単純なオブジェクト(2つのフィールド)のクローンを作成する平均時間。デフォルトのパブリックコンストラクターはありません。10,000回実行します。
高速であることに加えて、clonerを選択する理由は次のとおりです。
使いやすい。例:
cloner.deepClone(anyObject);
私は、Brad が提示したクローン ライブラリの作成者です。これは、余分なコードを書く必要のないオブジェクトのクローンを作成するためのソリューションです (シリアライズ可能なオブジェクトや impl clone() メソッドは必要ありません)。
Brad が言ったように非常に高速で、最近、さらに高速なバージョンをアップロードしました。clone() メソッドを手動で実装すると、Clone lib よりも高速になりますが、多くのコードを記述する必要があることに注意してください。
Cloner lib は、トラフィックが非常に多い (1 日あたり約 100 万リクエスト) サイトのキャッシュ実装で使用しているため、非常にうまく機能しています。キャッシュは、リクエストごとに約 10 個のオブジェクトを複製する必要があります。それは非常に信頼性が高く安定しています。ただし、クローン作成にはリスクがないわけではないことに注意してください。lib は、開発中に複製するすべてのクラス インスタンスを println するように構成できます。このようにして、クローンする必要があると思われるものをクローンするかどうかを確認できます。オブジェクト グラフは非常に深く、驚くほど大量のオブジェクトへの参照を含むことができます。clone lib を使用すると、不要なオブジェクト (シングルトンなど) を複製しないように指示できます。
任意のコレクションをディープ クローンする一般的な方法の 1 つは、それをストリームにシリアライズしてから、新しいコレクションに読み戻すことです。同一のコピーである以外は、古いオブジェクトとは何の関係もない、完全に新しいオブジェクトを復元します。
Apache Commons serialization utility classesへのリンクについては、Bruno の回答を確認してください。
1 つの可能性は、シリアライゼーションを使用することです。
Apache Commons はSerializationUtilsを提供します
私はこの複製ライブラリを使用しましたが、非常に便利であることがわかりました。いくつかの制限があったため (どのフィールド、どのコンテキストで、どの程度の深さで複製する必要があるかなど、複製プロセスをより細かく制御する必要がありました)、拡張バージョンを作成しました。エンティティ クラスでフィールドに注釈を付けることで、フィールドの複製を制御します。
雰囲気をつかむために、クラスの例を次に示します。
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
必要な場合に備えて、休止状態固有の拡張機能もあります。
シリアライゼーションとデシリアライゼーションを使用しますが、このアプローチは一時フィールドを持たないシリアライズ可能クラスでのみ機能することに注意してください。また、シングルトンはシングルトンではなくなります。