オブジェクトの深いコピー機能を実装するのは少し難しいです。元のオブジェクトと複製されたオブジェクトが参照を共有しないようにするためにどのような手順を実行しますか?
オブジェクトの深いコピー機能を実装するのは少し難しいです。元のオブジェクトと複製されたオブジェクトが参照を共有しないようにするためにどのような手順を実行しますか?
回答:
安全な方法は、オブジェクトをシリアル化してから逆シリアル化することです。これにより、すべてがまったく新しいリファレンスになります。
これを効率的に行う方法についての記事を次に示します。
警告:クラスがシリアル化をオーバーライドして、新しいインスタンスが作成されないようにすることが可能です(シングルトンなど)。もちろん、クラスがSerializableでない場合、これは機能しません。
数人がの使用または上書きについて言及していますObject.clone()
。しないでください。Object.clone()
いくつかの大きな問題があり、ほとんどの場合、その使用は推奨されません。完全な回答については、Joshua Blochの「Effective Java」のアイテム11を参照してください。Object.clone()
プリミティブ型の配列で安全に使用できると思いますが、それとは別に、クローンの適切な使用とオーバーライドについて慎重に検討する必要があります。
シリアル化(XMLまたはその他)に依存するスキームは、扱いにくいです。
ここに簡単な答えはありません。オブジェクトをディープコピーする場合は、オブジェクトグラフをトラバースして、オブジェクトのコピーコンストラクターまたは静的ファクトリメソッドを介して各子オブジェクトを明示的にコピーし、次に子オブジェクトをディープコピーする必要があります。不変(例:String
s)をコピーする必要はありません。余談ですが、この理由から不変性を優先する必要があります。
ファイルを作成せずにシリアライゼーションでディープコピーを作成できます。
ディープコピーするオブジェクトはにする必要がありimplement 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 = (Object) new ObjectInputStream(bais).readObject();
instance
この場合、非シリアル化可能なクラスプロパティはどのようにシリアル化され、非シリアル化できますか?
org.apache.commons.lang3.SerializationUtils.clone(T)
Apache Commons Lang を使用して、シリアライゼーションベースのディープクローンを作成できますが、パフォーマンスはひどいです。
一般に、クローン作成が必要なオブジェクトグラフのオブジェクトのクラスごとに、独自のクローンメソッドを作成することをお勧めします。
org.apache.commons.lang.SerializationUtils
ディープコピーを実装する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;
}
}
編集:コピーコンストラクターを使用する場合は、コピーするオブジェクトの実行時の型を知る必要があることに注意してください。上記のアプローチでは、混合リストを簡単にコピーすることはできません(いくつかのリフレクションコードでそれを行うことができる場合があります)。
Toyota
場合、コードはCar
宛先リストにを入れます。適切なクローン作成では、通常、クラスが仮想ファクトリメソッドを提供する必要があります。その契約では、独自のクラスの新しいオブジェクトを返すと規定されています。コピーコンストラクタ自体はprotected
、正確なタイプがコピーされるオブジェクトのタイプと一致するオブジェクトを構築するためにのみ使用されることを保証する必要があります)。
Apache commonsはオブジェクトをディープクローンする高速な方法を提供します。
My_Object object2= org.apache.commons.lang.SerializationUtils.clone(object1);
XStreamは、このような場合に非常に役立ちます。これはクローンを作成するための簡単なコードです
private static final XStream XSTREAM = new XStream();
...
Object newObject = XSTREAM.fromXML(XSTREAM.toXML(obj));
複雑なオブジェクトの場合、パフォーマンスがそれほど重要でない場合は、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);
}
XStream(http://x-stream.github.io/)を使用します。アノテーションを使用するか、XStreamクラスにプロパティ名を明示的に指定して、無視できるプロパティを制御することもできます。さらに、クローン可能なインターフェースを実装する必要はありません。
ディープコピーは、各クラスの同意がある場合にのみ実行できます。クラス階層を制御できる場合は、クローン可能インターフェースを実装し、Cloneメソッドを実装できます。そうでない場合、オブジェクトがデータ以外のリソース(データベース接続など)も共有している可能性があるため、ディープコピーを安全に行うことは不可能です。ただし、一般に、ディープコピーはJava環境では悪い習慣と見なされており、適切な設計手法を使用して回避する必要があります。
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));
}
}
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クラスはシリアライズ可能なインターフェースを実装する必要があります
Jacksonを使用してオブジェクトをシリアル化および逆シリアル化します。この実装では、オブジェクトがSerializableクラスを実装する必要はありません。
<T> T clone(T object, Class<T> clazzType) throws IOException {
final ObjectMapper objMapper = new ObjectMapper();
String jsonStr= objMapper.writeValueAsString(object);
return objMapper.readValue(jsonStr, clazzType);
}