Trochę trudno jest zaimplementować funkcję głębokiego kopiowania obiektów. Jakie kroki podejmujesz, aby zapewnić, że oryginalny obiekt i sklonowany obiekt nie mają odniesienia?
Ostrzeżenia: Możliwe jest, że klasy zastąpią serializację, tak że nowe instancje nie są tworzone, np. Dla singletonów. Również to oczywiście nie działa, jeśli twoich zajęć nie można szeregować.
Należy pamiętać, że implementacja FastByteArrayOutputStream podana w tym artykule może być bardziej wydajna. Korzysta z rozszerzenia w stylu ArrayList, gdy bufor się zapełnia, ale lepiej jest zastosować metodę rozszerzenia w stylu LinkedList. Zamiast tworzyć nowy bufor 2x i zapamiętywać bieżący bufor, utrzymuj połączoną listę buforów, dodając nowy, gdy bieżący się zapełni. Jeśli pojawi się prośba o zapisanie większej ilości danych niż mieści się w domyślnym rozmiarze bufora, utwórz węzeł bufora, który jest dokładnie tak duży jak żądanie; węzły nie muszą być tego samego rozmiaru.
@BrianHarris połączona lista nie jest bardziej wydajna niż tablica dynamiczna. Wstawianie elementów do tablicy dynamicznej jest amortyzowane stałą złożonością, podczas gdy wstawianie do połączonej listy jest złożonością liniową
Norill Tempest,
Ile serializacji i deserializacji wolniej niż podejście konstruktora kopiowania?
Woland
75
Kilka osób wspomniało o używaniu lub zastępowaniu Object.clone(). Nie rób tego Object.clone()ma pewne poważne problemy, a jego stosowanie jest w większości przypadków odradzane. Pełna odpowiedź znajduje się w punkcie 11 z „ Effective Java ” Joshua Blocha. Wierzę, że możesz bezpiecznie używać Object.clone()na tablicach typu pierwotnego, ale oprócz tego musisz rozsądnie traktować prawidłowe używanie i zastępowanie klonu.
Schematy oparte na serializacji (XML lub innej) są kludgy.
Nie ma tutaj łatwej odpowiedzi. Jeśli chcesz głęboko skopiować obiekt, musisz przejść przez wykres obiektu i skopiować każdy obiekt podrzędny jawnie za pomocą konstruktora obiektu lub statycznej metody fabrycznej, która z kolei głęboko kopiuje obiekt podrzędny. Niezmienne (np. StringS) nie muszą być kopiowane. Na marginesie, powinieneś faworyzować niezmienność z tego powodu.
Możesz wykonać głęboką kopię z serializacją bez tworzenia plików.
Twój obiekt, który chcesz skopiować, będzie musiał implement serializable. Jeśli klasa nie jest ostateczna lub nie można jej zmodyfikować, rozszerz ją i zaimplementuj możliwość serializacji.
Jest również dostępny worg.apache.commons.lang.SerializationUtils
Pino
25
Jednym ze sposobów implementacji głębokiego kopiowania jest dodanie konstruktorów kopiowania do każdej powiązanej klasy. Konstruktor kopiujący przyjmuje instancję „tego” jako swój pojedynczy argument i kopiuje z niej wszystkie wartości. Dość trochę pracy, ale całkiem proste i bezpieczne.
EDYCJA: pamiętaj, że nie musisz używać metod dostępu do odczytu pól. Możesz uzyskać bezpośredni dostęp do wszystkich pól, ponieważ instancja źródłowa jest zawsze tego samego typu co instancja z konstruktorem kopiowania. Oczywiste, ale można je przeoczyć.
Edycja: Zauważ, że podczas korzystania z konstruktorów kopiowania musisz znać typ środowiska wykonawczego kopiowanego obiektu. Przy powyższym podejściu nie można łatwo skopiować listy mieszanej (możesz to zrobić za pomocą kodu refleksyjnego).
Po prostu interesuje mnie przypadek, że to, co kopiujesz, jest podklasą, ale nadrzędnym jest odniesienie. Czy można zastąpić konstruktor kopiowania?
Pork 'n' Bunny
Dlaczego twoja klasa nadrzędna odwołuje się do swojej podklasy? Czy możesz podać przykład?
Adriaan Koster,
1
klasa publiczna Samochód rozszerza pojazd, a następnie odnosi się do samochodu jako pojazdu. originaList = new ArrayList <Vehicle>; copyList = new ArrayList <Vehicle>; originalList.add (new Car ()); dla (Vehicle pojazdu: VehicleList) {copyList.add (nowy pojazd (pojazd)); }
Pork 'n' Bunny
@AdriaanKoster: Jeśli oryginalna lista zawiera Toyota, Twój kod umieści Carna liście docelowej. Właściwe klonowanie wymaga na ogół, aby klasa dostarczyła metodę wirtualnej fabryki, której umowa stanowi, że zwróci nowy obiekt własnej klasy; sam konstruktor kopii powinien protectedzapewnić, że będzie on używany tylko do konstruowania obiektów, których dokładny typ odpowiada typowi kopiowanego obiektu).
supercat
Więc jeśli dobrze rozumiem twoją sugestię, metoda fabryczna wywołałaby konstruktora kopii prywatnej? W jaki sposób konstruktor kopii podklasy upewniałby się, że pola nadklasy są inicjowane? Czy możesz podać przykład?
Adriaan Koster
20
Możesz użyć biblioteki, która ma prosty interfejs API i wykonuje stosunkowo szybkie klonowanie z odbiciem (powinno być szybsze niż metody serializacji).
Cloner cloner =newCloner();MyClass clone = cloner.deepClone(o);// clone is a deep-clone of o
@egeleve Zdajesz sobie sprawę, że odpowiadasz na komentarz z '08 prawda? Nie używam już Java i prawdopodobnie są teraz lepsze narzędzia. Jednak w tym czasie serializowanie do innego formatu, a następnie serializowanie z powrotem wydawało się dobrym hackem - było zdecydowanie nieefektywne.
sankara
10
Jednym bardzo łatwym i prostym podejściem jest użycie Jackson JSON do serializacji złożonego obiektu Java do JSON i ponowne odczytanie go.
Dla użytkowników Spring Framework . Korzystanie z klasy org.springframework.util.SerializationUtils:
@SuppressWarnings("unchecked")publicstatic<T extendsSerializable> T clone(T object){return(T)SerializationUtils.deserialize(SerializationUtils.serialize(object));}
Do skomplikowanych obiektów i gdy wydajność nie jest znacząca, używam biblioteki json, takiej jak gson
aby serializować obiekt do tekstu json, a następnie deserializować tekst, aby uzyskać nowy obiekt.
gson, który oparty na odbiciu będzie działał w większości przypadków, z tym wyjątkiem, że transientpola nie zostaną skopiowane, a obiekty z referencją cykliczną z podaniem przyczyny StackOverflowError.
publicstatic<T> T copy(T anObject,Class<T> classInfo){Gson gson =newGsonBuilder().create();String text = gson.toJson(anObject);
T newObject = gson.fromJson(text, classInfo);return newObject;}publicstaticvoid main(String[] args){String originalObject ="hello";String copiedObject = copy(originalObject,String.class);}
Przestrzegaj konwencji nazewnictwa Java dla własnego i naszego dobra.
Patrick Bergner,
8
Użyj XStream ( http://x-stream.github.io/ ). Możesz nawet kontrolować, które właściwości możesz zignorować za pomocą adnotacji lub jawnie podając nazwę właściwości do klasy XStream. Ponadto nie trzeba implementować klonowalnego interfejsu.
Głębokiego kopiowania można dokonać tylko za zgodą każdej klasy. Jeśli masz kontrolę nad hierarchią klas, możesz zaimplementować klonowalny interfejs i zaimplementować metodę klonowania. W przeciwnym razie robienie głębokiej kopii nie jest bezpieczne, 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 należy tego unikać poprzez odpowiednie praktyki projektowe.
Odpowiedzi:
Bezpiecznym sposobem jest serializacja obiektu, a następnie deserializacja. To gwarantuje, że wszystko jest zupełnie nowym odniesieniem.
Oto artykuł o tym, jak to zrobić skutecznie.
Ostrzeżenia: Możliwe jest, że klasy zastąpią serializację, tak że nowe instancje nie są tworzone, np. Dla singletonów. Również to oczywiście nie działa, jeśli twoich zajęć nie można szeregować.
źródło
Kilka osób wspomniało o używaniu lub zastępowaniu
Object.clone()
. Nie rób tegoObject.clone()
ma pewne poważne problemy, a jego stosowanie jest w większości przypadków odradzane. Pełna odpowiedź znajduje się w punkcie 11 z „ Effective Java ” Joshua Blocha. Wierzę, że możesz bezpiecznie używaćObject.clone()
na tablicach typu pierwotnego, ale oprócz tego musisz rozsądnie traktować prawidłowe używanie i zastępowanie klonu.Schematy oparte na serializacji (XML lub innej) są kludgy.
Nie ma tutaj łatwej odpowiedzi. Jeśli chcesz głęboko skopiować obiekt, musisz przejść przez wykres obiektu i skopiować każdy obiekt podrzędny jawnie za pomocą konstruktora obiektu lub statycznej metody fabrycznej, która z kolei głęboko kopiuje obiekt podrzędny. Niezmienne (np.
String
S) nie muszą być kopiowane. Na marginesie, powinieneś faworyzować niezmienność z tego powodu.źródło
Możesz wykonać głęboką kopię z serializacją bez tworzenia plików.
Twój obiekt, który chcesz skopiować, będzie musiał
implement serializable
. Jeśli klasa nie jest ostateczna lub nie można jej zmodyfikować, rozszerz ją i zaimplementuj możliwość serializacji.Konwertuj klasę na strumień bajtów:
Przywróć klasę ze strumienia bajtów:
źródło
instance
w tym przypadku niemożliwa do serializacji ?Możesz wykonać głęboki klon oparty na serializacji za pomocą
org.apache.commons.lang3.SerializationUtils.clone(T)
Apache Commons Lang, ale bądź ostrożny - wydajność jest fatalna.Ogólnie rzecz biorąc, najlepszą praktyką jest pisanie własnych metod klonowania dla każdej klasy obiektu na grafie obiektowym wymagającym klonowania.
źródło
org.apache.commons.lang.SerializationUtils
Jednym ze sposobów implementacji głębokiego kopiowania jest dodanie konstruktorów kopiowania do każdej powiązanej klasy. Konstruktor kopiujący przyjmuje instancję „tego” jako swój pojedynczy argument i kopiuje z niej wszystkie wartości. Dość trochę pracy, ale całkiem proste i bezpieczne.
EDYCJA: pamiętaj, że nie musisz używać metod dostępu do odczytu pól. Możesz uzyskać bezpośredni dostęp do wszystkich pól, ponieważ instancja źródłowa jest zawsze tego samego typu co instancja z konstruktorem kopiowania. Oczywiste, ale można je przeoczyć.
Przykład:
Edycja: Zauważ, że podczas korzystania z konstruktorów kopiowania musisz znać typ środowiska wykonawczego kopiowanego obiektu. Przy powyższym podejściu nie można łatwo skopiować listy mieszanej (możesz to zrobić za pomocą kodu refleksyjnego).
źródło
Toyota
, Twój kod umieściCar
na liście docelowej. Właściwe klonowanie wymaga na ogół, aby klasa dostarczyła metodę wirtualnej fabryki, której umowa stanowi, że zwróci nowy obiekt własnej klasy; sam konstruktor kopii powinienprotected
zapewnić, że będzie on używany tylko do konstruowania obiektów, których dokładny typ odpowiada typowi kopiowanego obiektu).Możesz użyć biblioteki, która ma prosty interfejs API i wykonuje stosunkowo szybkie klonowanie z odbiciem (powinno być szybsze niż metody serializacji).
źródło
Apache commons oferuje szybki sposób głębokiego klonowania obiektu.
źródło
XStream jest naprawdę przydatny w takich przypadkach. Oto prosty kod do klonowania
źródło
Jednym bardzo łatwym i prostym podejściem jest użycie Jackson JSON do serializacji złożonego obiektu Java do JSON i ponowne odczytanie go.
http://wiki.fasterxml.com/JacksonInFiveMinutes
źródło
Dla użytkowników Spring Framework . Korzystanie z klasy
org.springframework.util.SerializationUtils
:źródło
Do skomplikowanych obiektów i gdy wydajność nie jest znacząca, używam biblioteki json, takiej jak gson aby serializować obiekt do tekstu json, a następnie deserializować tekst, aby uzyskać nowy obiekt.
gson, który oparty na odbiciu będzie działał w większości przypadków, z tym wyjątkiem, że
transient
pola nie zostaną skopiowane, a obiekty z referencją cykliczną z podaniem przyczynyStackOverflowError
.źródło
Użyj XStream ( http://x-stream.github.io/ ). Możesz nawet kontrolować, które właściwości możesz zignorować za pomocą adnotacji lub jawnie podając nazwę właściwości do klasy XStream. Ponadto nie trzeba implementować klonowalnego interfejsu.
źródło
Głębokiego kopiowania można dokonać tylko za zgodą każdej klasy. Jeśli masz kontrolę nad hierarchią klas, możesz zaimplementować klonowalny interfejs i zaimplementować metodę klonowania. W przeciwnym razie robienie głębokiej kopii nie jest bezpieczne, 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 należy tego unikać poprzez odpowiednie praktyki projektowe.
źródło
źródło
Użyłem Dozera do klonowania obiektów java i jest w tym świetny, biblioteka Kryo to kolejna świetna alternatywa.
źródło
BeanUtils wykonuje naprawdę dobrą robotę, głęboko klonując fasolę.
źródło
1)
W tym przypadku klasa MyPerson i MyAddress musi implementować interfejs z możliwością szeregowania
źródło
Używanie Jacksona do serializacji i deserializacji obiektu. Ta implementacja nie wymaga, aby obiekt zaimplementował klasę Serializable.
źródło