Немного сложно реализовать функцию глубокого копирования объектов. Какие шаги вы предпринимаете, чтобы гарантировать, что исходный и клонированный объекты не имеют общего доступа?
Предостережения: классы могут переопределить сериализацию, чтобы новые экземпляры не создавались, например, для одиночных игр. Кроме того, это, конечно, не работает, если ваши классы не Serializable.
Помните, что реализация FastByteArrayOutputStream, представленная в статье, может быть более эффективной. Он использует расширение в стиле ArrayList, когда буфер заполняется, но лучше использовать подход расширения в стиле LinkedList. Вместо создания нового 2x буфера и запоминания текущего буфера, ведите связанный список буферов, добавляя новый, когда текущий заполняется. Если вы получили запрос на запись большего количества данных, чем уместилось бы в вашем размере буфера по умолчанию, создайте буферный узел, который будет точно таким же, как запрос; узлы не должны быть одинакового размера.
Связанный список @BrianHarris не более эффективен, чем динамический массив. Вставка элементов в динамический массив амортизируется постоянной сложностью, а вставка в связанный список - линейной сложностью
Norill Tempest
Насколько сериализация и десериализация медленнее подхода конструктора копирования?
Воланд
75
Несколько человек упомянули об использовании или переопределении Object.clone(). Не делай этого. Object.clone()имеет некоторые серьезные проблемы, и его использование не рекомендуется в большинстве случаев. Пожалуйста, см. Пункт 11 из « Эффективной Явы » Джошуа Блоха для полного ответа. Я полагаю, что вы можете безопасно использовать Object.clone()массивы примитивного типа, но помимо этого вы должны быть осторожны в правильном использовании и переопределении клона.
Схемы, которые полагаются на сериализацию (XML или иным образом), являются хитрыми.
Здесь нет простого ответа. Если вы хотите выполнить глубокое копирование объекта, вам придется пройти по графу объектов и явно скопировать каждый дочерний объект с помощью конструктора копирования объекта или статического метода фабрики, который, в свою очередь, копирует дочерний объект. Неизменяемые (например, Strings) не должны быть скопированы. Кроме того, вы должны поддержать неизменность по этой причине.
Вы можете сделать глубокую копию с сериализацией без создания файлов.
Ваш объект, который вы хотите глубоко скопировать, потребуется implement serializable. Если класс не является окончательным или не может быть изменен, расширьте класс и реализуйте сериализуемый.
Это также доступно вorg.apache.commons.lang.SerializationUtils
Пино
25
Одним из способов реализации глубокого копирования является добавление конструкторов копирования в каждый связанный класс. Конструктор копирования принимает экземпляр this в качестве единственного аргумента и копирует все значения из него. Довольно трудоемко, но довольно просто и безопасно.
РЕДАКТИРОВАТЬ: обратите внимание, что вам не нужно использовать методы доступа для чтения полей. Вы можете получить доступ ко всем полям напрямую, потому что исходный экземпляр всегда имеет тот же тип, что и экземпляр с конструктором копирования. Очевидно, но может быть упущено.
Изменить: Обратите внимание, что при использовании конструкторов копирования вам необходимо знать тип времени выполнения объекта, который вы копируете. При описанном выше подходе вы не можете легко скопировать смешанный список (вы можете сделать это с помощью некоторого кода отражения).
Просто интересно, что то, что вы копируете, является подклассом, но на него ссылается родитель. Можно ли переопределить конструктор копирования?
Pork 'n' Bunny
Почему ваш родительский класс ссылается на свой подкласс? Можете привести пример?
Адриан Костер
1
Автомобиль общего класса расширяет автомобиль, а затем ссылается на автомобиль как транспортное средство. originaList = новый ArrayList <Автомобиль>; copyList = new ArrayList <Vehicle>; originalList.add (new Car ()); for (Vehicle Vehicle: vehicleList) {copyList.add (новое Транспортное средство (транспортное средство)); }
Pork 'n' Bunny
@AdriaanKoster: Если исходный список содержит Toyota, ваш код будет помещен Carв список адресатов. Правильное клонирование обычно требует, чтобы класс предоставлял метод виртуальной фабрики, контракт которого гласит, что он возвратит новый объект своего собственного класса; сам конструктор копирования должен protectedгарантировать, что он будет использоваться только для создания объектов, точный тип которых соответствует типу копируемого объекта).
суперкат
Так что, если я правильно понимаю ваше предложение, фабричный метод вызовет конструктор приватной копии? Как конструктор копирования подкласса должен убедиться, что поля суперкласса инициализированы? Можете привести пример?
Адриан Костер
20
Вы можете использовать библиотеку, которая имеет простой API и выполняет относительно быстрое клонирование с отражением (должно быть быстрее, чем методы сериализации).
Cloner cloner =newCloner();MyClass clone = cloner.deepClone(o);// clone is a deep-clone of o
Нееет, вам не нужны накладные расходы на XML-объект.
Эгелев
@egeleve Вы понимаете, что отвечаете на комментарий от '08, верно? Я больше не использую Java, и сейчас, вероятно, есть лучшие инструменты. Однако в то время сериализация в другой формат и последующая обратная сериализация казались хорошим взломом - это было определенно неэффективно.
Шанкара
10
Одним из очень простых и простых подходов является использование Jackson JSON для сериализации сложного Java Object в JSON и его последующего чтения.
Для пользователей Spring Framework . Используя класс org.springframework.util.SerializationUtils:
@SuppressWarnings("unchecked")publicstatic<T extendsSerializable> T clone(T object){return(T)SerializationUtils.deserialize(SerializationUtils.serialize(object));}
Для сложных объектов и когда производительность незначительна, я использую библиотеку json, например, gson,
для сериализации объекта в текст json, а затем десериализации текста для получения нового объекта.
gson, основанный на отражении, будет работать в большинстве случаев, за исключением того, что transientполя не будут скопированы и объекты с круговой ссылкой с причиной 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);}
Пожалуйста, придерживайтесь соглашений об именах Java для себя и ради нас.
Патрик Бергнер
8
Используйте XStream ( http://x-stream.github.io/ ). Вы даже можете контролировать, какие свойства вы можете игнорировать с помощью аннотаций или явного указания имени свойства для класса XStream. Более того, вам не нужно реализовывать клонируемый интерфейс.
Глубокое копирование может быть сделано только с согласия каждого класса. Если у вас есть контроль над иерархией классов, вы можете реализовать клонируемый интерфейс и реализовать метод Clone. В противном случае безопасное копирование невозможно сделать безопасно, поскольку объект может также совместно использовать ресурсы, не относящиеся к данным (например, соединения с базой данных). В целом, однако, глубокое копирование считается плохой практикой в среде Java, и его следует избегать с помощью соответствующих методов проектирования.
Ответы:
Безопасным способом является сериализация объекта, а затем десериализация. Это гарантирует, что все является новой ссылкой.
Вот статья о том, как сделать это эффективно.
Предостережения: классы могут переопределить сериализацию, чтобы новые экземпляры не создавались, например, для одиночных игр. Кроме того, это, конечно, не работает, если ваши классы не Serializable.
источник
Несколько человек упомянули об использовании или переопределении
Object.clone()
. Не делай этого.Object.clone()
имеет некоторые серьезные проблемы, и его использование не рекомендуется в большинстве случаев. Пожалуйста, см. Пункт 11 из « Эффективной Явы » Джошуа Блоха для полного ответа. Я полагаю, что вы можете безопасно использоватьObject.clone()
массивы примитивного типа, но помимо этого вы должны быть осторожны в правильном использовании и переопределении клона.Схемы, которые полагаются на сериализацию (XML или иным образом), являются хитрыми.
Здесь нет простого ответа. Если вы хотите выполнить глубокое копирование объекта, вам придется пройти по графу объектов и явно скопировать каждый дочерний объект с помощью конструктора копирования объекта или статического метода фабрики, который, в свою очередь, копирует дочерний объект. Неизменяемые (например,
String
s) не должны быть скопированы. Кроме того, вы должны поддержать неизменность по этой причине.источник
Вы можете сделать глубокую копию с сериализацией без создания файлов.
Ваш объект, который вы хотите глубоко скопировать, потребуется
implement serializable
. Если класс не является окончательным или не может быть изменен, расширьте класс и реализуйте сериализуемый.Преобразуйте ваш класс в поток байтов:
Восстановите ваш класс из потока байтов:
источник
instance
в этом случае?Вы можете сделать глубокий клон на основе сериализации, используя
org.apache.commons.lang3.SerializationUtils.clone(T)
Apache Commons Lang, но будьте осторожны - производительность ужасна.В целом, рекомендуется писать собственные методы клонирования для каждого класса объекта в графе объектов, нуждающихся в клонировании.
источник
org.apache.commons.lang.SerializationUtils
Одним из способов реализации глубокого копирования является добавление конструкторов копирования в каждый связанный класс. Конструктор копирования принимает экземпляр this в качестве единственного аргумента и копирует все значения из него. Довольно трудоемко, но довольно просто и безопасно.
РЕДАКТИРОВАТЬ: обратите внимание, что вам не нужно использовать методы доступа для чтения полей. Вы можете получить доступ ко всем полям напрямую, потому что исходный экземпляр всегда имеет тот же тип, что и экземпляр с конструктором копирования. Очевидно, но может быть упущено.
Пример:
Изменить: Обратите внимание, что при использовании конструкторов копирования вам необходимо знать тип времени выполнения объекта, который вы копируете. При описанном выше подходе вы не можете легко скопировать смешанный список (вы можете сделать это с помощью некоторого кода отражения).
источник
Toyota
, ваш код будет помещенCar
в список адресатов. Правильное клонирование обычно требует, чтобы класс предоставлял метод виртуальной фабрики, контракт которого гласит, что он возвратит новый объект своего собственного класса; сам конструктор копирования долженprotected
гарантировать, что он будет использоваться только для создания объектов, точный тип которых соответствует типу копируемого объекта).Вы можете использовать библиотеку, которая имеет простой API и выполняет относительно быстрое клонирование с отражением (должно быть быстрее, чем методы сериализации).
источник
Apache commons предлагает быстрый способ глубокого клонирования объекта.
источник
XStream действительно полезен в таких случаях. Вот простой код для клонирования
источник
Одним из очень простых и простых подходов является использование Jackson JSON для сериализации сложного Java Object в JSON и его последующего чтения.
http://wiki.fasterxml.com/JacksonInFiveMinutes
источник
Для пользователей Spring Framework . Используя класс
org.springframework.util.SerializationUtils
:источник
Для сложных объектов и когда производительность незначительна, я использую библиотеку json, например, gson, для сериализации объекта в текст json, а затем десериализации текста для получения нового объекта.
gson, основанный на отражении, будет работать в большинстве случаев, за исключением того, что
transient
поля не будут скопированы и объекты с круговой ссылкой с причинойStackOverflowError
.источник
Используйте XStream ( http://x-stream.github.io/ ). Вы даже можете контролировать, какие свойства вы можете игнорировать с помощью аннотаций или явного указания имени свойства для класса XStream. Более того, вам не нужно реализовывать клонируемый интерфейс.
источник
Глубокое копирование может быть сделано только с согласия каждого класса. Если у вас есть контроль над иерархией классов, вы можете реализовать клонируемый интерфейс и реализовать метод Clone. В противном случае безопасное копирование невозможно сделать безопасно, поскольку объект может также совместно использовать ресурсы, не относящиеся к данным (например, соединения с базой данных). В целом, однако, глубокое копирование считается плохой практикой в среде Java, и его следует избегать с помощью соответствующих методов проектирования.
источник
источник
Я использовал Dozer для клонирования объектов Java, и это здорово, библиотека Kryo - еще одна отличная альтернатива.
источник
BeanUtils отлично справляется с глубоким клонированием бобов.
источник
1)
Здесь ваш класс MyPerson и MyAddress должны реализовывать интерфейс Serilazable
источник
Использование Джексона для сериализации и десериализации объекта. Эта реализация не требует, чтобы объект реализовал класс Serializable.
источник