Jak zrobić głęboką kopię obiektu?
Implementacja funkcji głębokiego kopiowania obiektów jest nieco trudna. Jakie kroki należy podjąć, aby upewnić się, że oryginalny obiekt i sklonowany nie mają żadnego odniesienia?
20 answers
Bezpiecznym sposobem jest serializacja obiektu, a następnie deserializacja. Zapewnia to, że wszystko jest zupełnie nowym punktem odniesienia.
Oto Artykuł o tym, jak to zrobić skutecznie.
Zastrzeżenia: możliwe jest, aby klasy nadpisywały serializację tak, że nowe instancje są tworzone , a nie, np. dla singletonów. Również to oczywiście nie działa, jeśli Twoje klasy nie są Serializowalne.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-01 09:23:49
Kilka osób wspomniało o używaniu lub nadpisywaniu Object.clone()
. Nie rób tego. Object.clone()
ma kilka poważnych problemów, a jego stosowanie jest zniechęcające w większości przypadków. Pełna odpowiedź znajduje się w punkcie 11 z "Effective Java" Joshuy Bloch. Wierzę, że można bezpiecznie używać Object.clone()
Na prymitywnych tablicach typu, ale poza tym trzeba rozsądnie używać i nadpisywać klon.
Schematy, które opierają się na serializacji (XML lub w inny sposób), są kludgy.
There is no easy odpowiedz tutaj. Jeśli chcesz głęboko skopiować obiekt, musisz przejść przez wykres obiektu i skopiować każdy obiekt potomny jawnie za pomocą konstruktora kopiującego obiektu lub statycznej metody fabrycznej, która z kolei głęboko kopiuje obiekt potomny. Obiekty niezmienne (np. String
s) nie muszą być kopiowane. Z tego powodu powinieneś faworyzować niezmienność.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-13 07:50:46
Możesz zrobić głęboką kopię z serializacją bez tworzenia plików.
Twój obiekt, który chcesz głęboko skopiować, będzie musiał implement serializable
. Jeśli klasa nie jest ostateczna lub nie może zostać zmodyfikowana, rozszerz klasę i zaimplementuj serializable.
Konwertuj klasę na strumień bajtów:
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(object);
oos.flush();
oos.close();
bos.close();
byte[] byteData = bos.toByteArray();
Przywróć klasę ze strumienia bajtów:
ByteArrayInputStream bais = new ByteArrayInputStream(byteData);
(Object) object = (Object) new ObjectInputStream(bais).readObject();
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-07 13:22:57
Możesz wykonać głęboki klon oparty na serializacji za pomocą org.apache.commons.lang3.SerializationUtils.clone(T)
w Apache Commons Lang, ale uważaj-wydajność jest fatalna.
Ogólnie rzecz biorąc, najlepszą praktyką jest pisanie własnych metod klonowania dla każdej klasy obiektu w grafie obiektów wymagających klonowania.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-09 14:59:52
Jednym ze sposobów implementacji głębokiej kopii jest dodanie konstruktorów kopiujących do każdej powiązanej klasy. Konstruktor kopiujący pobiera instancję 'this' jako swój pojedynczy argument i kopiuje z niego wszystkie wartości. Trochę pracy, ale całkiem proste i bezpieczne.
EDIT: zauważ, że nie musisz używać metod dostępu do odczytu pól. Możesz uzyskać dostęp do wszystkich pól bezpośrednio, ponieważ instancja źródłowa jest zawsze tego samego typu co instancja z konstruktorem kopiującym. Oczywiste, ale może być / align = "left" /
Przykład:
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;
}
}
Edit: zauważ, że podczas używania konstruktorów kopiujących musisz znać typ runtime kopiowanego obiektu. Przy powyższym podejściu nie można łatwo skopiować listy mieszanej (być może uda się to zrobić za pomocą kodu refleksyjnego).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-27 07:50:30
Możesz użyć biblioteki , która ma proste API i wykonuje stosunkowo szybkie klonowanie z odbiciem (powinno być szybsze niż metody serializacji).
Cloner cloner = new Cloner();
MyClass clone = cloner.deepClone(o);
// clone is a deep-clone of o
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-13 07:51:08
Apache commons oferuje szybki sposób na głębokie sklonowanie obiektu.
My_Object object2= org.apache.commons.lang.SerializationUtils.clone(object1);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-28 14:53:24
Dla Spring Framework użytkowników. Klasa org.springframework.util.SerializationUtils
:
@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(T object) {
return (T) SerializationUtils.deserialize(SerializationUtils.serialize(object));
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-19 22:15:18
XStream jest naprawdę przydatny w takich przypadkach. Oto prosty kod do klonowania
private static final XStream XSTREAM = new XStream();
...
Object newObject = XSTREAM.fromXML(XSTREAM.toXML(obj));
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-23 08:03:08
Do skomplikowanych obiektów i gdy wydajność nie jest znacząca używam biblioteki json, jak gson aby serializować obiekt do tekstu json, następnie deserializować tekst, aby uzyskać nowy obiekt.
Gson, który bazuje na odbiciu będzie działał w większości przypadków, z tą różnicą, że transient
Pola nie będą kopiowane, a obiekty z kolistym odniesieniem z przyczyną 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);
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-02-01 16:15:24
Jednym bardzo łatwym i prostym podejściem jest użycie Jacksona JSONA do serializacji złożonego obiektu Javy do JSON i odczytania go z powrotem.
Z https://github.com/FasterXML/jackson-databind/#5-minute-tutorial-streaming-parser-generator :
JsonFactory f = mapper.getFactory(); // may alternatively construct directly too
// First: write simple JSON output
File jsonFile = new File("test.json");
JsonGenerator g = f.createGenerator(jsonFile);
// write JSON: { "message" : "Hello world!" }
g.writeStartObject();
g.writeStringField("message", "Hello world!");
g.writeEndObject();
g.close();
// Second: read file back
JsonParser p = f.createParser(jsonFile);
JsonToken t = p.nextToken(); // Should be JsonToken.START_OBJECT
t = p.nextToken(); // JsonToken.FIELD_NAME
if ((t != JsonToken.FIELD_NAME) || !"message".equals(p.getCurrentName())) {
// handle error
}
t = p.nextToken();
if (t != JsonToken.VALUE_STRING) {
// similarly
}
String msg = p.getText();
System.out.printf("My message to you is: %s!\n", msg);
p.close();
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-10-12 04:19:15
Use XStream ( http://x-stream.github.io/). możesz nawet kontrolować, które właściwości możesz ignorować poprzez adnotacje lub jawne podanie nazwy właściwości do klasy XStream. Co więcej, nie musisz implementować interfejsu klonowalnego.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-02 11:35:00
Głębokie kopiowanie może być wykonane tylko za zgodą każdej klasy. Jeśli masz kontrolę nad hierarchią klas, możesz zaimplementować interfejs klonowalny i zaimplementować metodę klonowania. W przeciwnym razie wykonanie głębokiej kopii jest niemożliwe do zrobienia bezpiecznie, ponieważ obiekt może również współdzielić zasoby inne niż dane (np. połączenia z bazą danych). Ogólnie jednak głębokie kopiowanie jest uważane za złą praktykę w środowisku Java i powinno być unikane poprzez odpowiednie praktyki projektowe.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-15 15:44:32
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));
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-21 00:58:35
Użyłem Dozer do klonowania obiektów Javy i jest w tym świetny, Kryo Biblioteka jest kolejną świetną alternatywą.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-17 02:08:59
Używanie Jacksona do serializacji i deserializacji obiektu. Ta implementacja nie wymaga od obiektu implementacji klasy 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);
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-04-13 19:05:06
BeanUtils robi naprawdę dobrą robotę Głębokie klonowanie fasoli.
BeanUtils.cloneBean(obj);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-16 10:24:08
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);
Tutaj twoja klasa MyPerson i myaddress musi zaimplementować interfejs serilazable
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-11 14:51:53
Oto prosty przykład jak głęboko sklonować dowolny obiekt: Zaimplementuj serializable first
public class CSVTable implements Serializable{
Table<Integer, Integer, String> table;
public CSVTable() {
this.table = HashBasedTable.create();
}
public CSVTable deepClone() {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
return (CSVTable) ois.readObject();
} catch (IOException e) {
return null;
} catch (ClassNotFoundException e) {
return null;
}
}
}
A następnie
CSVTable table = new CSVTable();
CSVTable tempTable = table.deepClone();
W ten sposób dostajesz klona.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-08-28 20:00:53
Oto ogólna metoda głębokiego klonowania wykorzystująca serializację obiektów i deserializację ze strumieniami tablicy bajtów (aby uniknąć zapisu do pliku).
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
@SuppressWarnings("unchecked")
public static <T extends Serializable> T deepClone(T t) {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);) {
oos.writeObject(t);
byte[] bytes = baos.toByteArray();
try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes))) {
return (T) ois.readObject();
}
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2021-01-05 18:58:52