Java:インスタンスのディープクローニング/コピーの推奨ソリューション


176

Javaでインスタンスの深いクローン/コピーを行う推奨方法があるかどうか疑問に思っています。

私は3つの解決策を考えていますが、いくつかは見逃す可能性があります。あなたの意見を聞きたいと思います

編集:Bohzoの提案を含め、質問を洗練させます。これは、浅い複製よりも深い複製についてです。

自分でやれ:

プロパティの後に手動でクローンプロパティをコーディングし、可変インスタンスもクローンされることを確認します。
pro:
-
実行される内容の制御-迅速な実行
短所:
-作成および保守が面倒
-バグが発生しやすい(コピー/貼り付けの失敗、プロパティの欠落、変更可能なプロパティの再割り当て)

リフレクションを使用する:

独自のリフレクションツールまたは外部ヘルパー(jakarta common-beansなど)を使用すると、1行で処理を行う汎用のコピーメソッドを簡単に作成できます。
pro:
-書き込みが簡単
-メンテナンスの
短所:
-何が起きるかの制御が少ない
-リフレクションツールがサブオブジェクトも複製しない場合、ミュータブルオブジェクトでバグが発生しやすい
-実行が遅い

クローンフレームワークを使用する:

:のように、あなたのためにそれを行うフレームワークを使用してください
コモンズ・ラングSerializationUtils
Javaの深クローニングライブラリ
ドーザ
Kryo

プロ:
-リフレクションと同じ
-正確に複製されるものをより詳細に制御します。
短所:
-階層の最後でも、すべての可変インスタンスが完全に複製されます
-実行が非常に遅くなる可能性があります

実行時にバイトコードインストルメンテーションを使用してクローンを書き込む

javassitBCEL、またはcglibを使用すると、片手で書いたように高速で専用のクローンを生成できます。誰かがこの目的のためにこれらのツールの1つを使用しているlibを知っていますか?

ここで見逃したものは何ですか?
どちらをお勧めしますか?

ありがとう。


1
どうやらJavaディープクローニングライブラリはここに移動しました:code.google.com/p/cloning
Mr_and_Mrs_D

回答:


155

ディープクローニングの場合(オブジェクト階層全体を複製):

  • commons-lang SerializationUtils-シリアル化の使用-すべてのクラスがコントロール内にあり、実装を強制できる場合Serializable

  • Javaディープクローニングライブラリ -リフレクションの使用-クローンを作成するクラスまたはオブジェクトが制御不能(サードパーティライブラリ)で、それらを実装させることができないSerializable場合、または実装したくない場合Serializable

浅い複製の場合(最初のレベルのプロパティのみを複製):

私は「do-it-yourself」オプションを意図的に省略しました-上記のAPIは何をクローンし、何をクローンしないかを適切に制御するため(たとえばtransient、またはを使用String[] ignoreProperties)、ホイールを再発明することは好ましくありません。


Bozhoさん、ありがとうございます。そして、私はDIYオプションについてあなたに同意します!コモンズのシリアル化やディープクローニングライブラリを試したことはありますか?パフォーマンスについてはどうですか?
Guillaume

はい、上記の理由により、上記のすべてのオプションを使用しました:) CGLIBプロキシが関係する場合、クローンライブラリのみに問題があり、必要な機能がいくつかありませんでしたが、今は修正する必要があると思います。
Bozho 2010年

ねえ、私のエンティティが添付されていて怠惰なものがある場合、SerializationUtilsはデータベースで遅延プロパティをチェックしますか?これは私が欲しいものですが、そうではありません!
Cosmin Cosmin

アクティブなセッションがある場合-はい、そうです。
Bozho

@Bozhoつまり、Bean内のすべてのオブジェクトが直列化可能を実装している場合、org.apache.commons.beanutils.BeanUtils.cloneBean(obj)はディープコピーを実行しますか?
ホップ

36

Joshua Blochの本には「Item 10:Override Clone Judiciously」というタイトルの章があり、Java仕様で多くの問題が発生するため、ほとんどの場合、クローンのオーバーライドが悪い考えです。

彼はいくつかの選択肢を提供します:

  • コンストラクタの代わりにファクトリパターンを使用します。

         public static Yum newInstance(Yum yum);
  • コピーコンストラクターを使用します。

         public Yum(Yum yum);

Javaのすべてのコレクションクラスは、コピーコンストラクターをサポートします(例:new ArrayList(l);)


1
同意した。私のプロジェクトCopyableでは、getCopy()メソッドを含むインターフェースを定義しました。手動でプロトタイプパターンを使用するだけです。
gpampara 2010年

さて、私はクローン可能なインターフェースについて質問していませんでしたが、深いクローン/コピー操作を実行する方法を尋ねていました。コンストラクタまたはファクトリを使用する場合でも、ソースから新しいインスタンスを作成する必要があります。
Guillaume

@ギヨーム深いクローン/コピーという言葉を使うときは注意が必要だと思います。Javaでのクローンとコピーは同じ意味ではありません。これについては、Java仕様にもっと言及する必要があります...私が言えることから、深いコピーが欲しいと思います。
LeWoody 2010

OK Java仕様はクローンとは何かについて正確です...しかし、より一般的な意味でクローンについて話すこともできます...たとえば、bohzoが推奨するライブラリの1つは「Java Deep Cloning Library」という名前です...
ギヨーム

2
@LWoodyiiiこのnewInstance()メソッドとYumコンストラクターはディープコピーまたはシャローコピーを実行しますか?
オタク2013


5

XStream toXML / fromXMLをメモリで使用します。非常に速く、長い間使用されており、強くなっています。オブジェクトはSerializableである必要はなく、リフレクションを使用する必要はありません(XStreamはそうです)。XStreamは、同じオブジェクトを指す変数を識別でき、誤ってインスタンスの2つの完全なコピーを作成しません。そのような多くの詳細は長年にわたって打ち出されてきました。私はそれを何年も使用してきましたが、それは初めてです。想像できるほど簡単に使用できます。

new XStream().toXML(myObj)

または

new XStream().fromXML(myXML)

クローンするには、

new XStream().fromXML(new XStream().toXML(myObj))

より簡潔に:

XStream x = new XStream();
Object myClone = x.fromXML(x.toXML(myObj));

3

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

リフレクションに基づくgsonは、ほとんどの場合に機能しtransientます。ただし、フィールドはコピーされず、循環参照を持つオブジェクトはcauseを使用しStackOverflowErrorます。

public static <ObjectType> ObjectType Copy(ObjectType AnObject, Class<ObjectType> ClassInfo)
{
    Gson gson = new GsonBuilder().create();
    String text = gson.toJson(AnObject);
    ObjectType newObject = gson.fromJson(text, ClassInfo);
    return newObject;
}
public static void main(String[] args)
{
    MyObject anObject ...
    MyObject copyObject = Copy(o, MyObject.class);

}

2

依存します。

スピードについては、DIYを使用してください。防弾には、反射を使用します。

ところで、シリアライゼーションはreflと同じではありません。一部のオブジェクトはオーバーライドされたシリアライゼーションメソッド(readObject / writeObject)を提供することがあり、バグがある可能性があるためです。


1
リフレクションは完全な証拠ではありません。クローンされたオブジェクトがソースを参照している状況につながる可能性があります...ソースが変更されると、クローンも変更されます!
Guillaume、

1

DIYの方法をお勧めします。これは、優れたhashCode()およびequals()メソッドと組み合わせると、単体テストで簡単に証明できるはずです。


まあ、そのようなダミーコードを作成するとき、怠惰な私は多くのことを正当化します。しかし、それはより賢明な道のように見えます...
ギヨーム

2
申し訳ありませんが、DIYで行く方法されONLY、他のソリューションはyou..whichに適していない場合がほとんどになることはありません
Bozho

1

Object.clone()をオーバーライドし、最初にsuper.clone()を呼び出してから、ディープコピーするすべての参照でref = ref.clone()を呼び出すことをお勧めします。それは多かれ少なかれ自分でやりますが、少し少ないコーディングが必要です。


2
これは、(壊れた)cloneメソッドの多くの問題の1つです。クラス階層では、常にsuper.clone()を呼び出す必要があり、簡単に忘れることができるため、私はコピーコンストラクターを使用することを好みます。
ヘルパーメソッド2010年

0

ディープクローンの場合、このようにクローンを作成するすべてのクラスにSerializableを実装します。

public static class Obj implements Serializable {
    public int a, b;
    public Obj(int a, int b) {
        this.a = a;
        this.b = b;
    }
}

そして、この関数を使用します:

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;
    }
}

このような: Obj newObject = (Obj)deepClone(oldObject);

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.