2009-03-20 4 views
63

Есть ли утилита для глубокого клонирования для ява коллекций:Deep клонировать утилита рекомендация

  • Массивы
  • Списки
  • Карта

Примечание: предпочитает какое-то решение без использования сериализации, но с использованием метода Object.clone(). Я могу быть уверен, что мой пользовательский объект будет реализовывать метод Clone() и будет использовать только Java стандартные классы, клонируемыми ...

+0

Возможный дубликат [Как сделать глубокую копию объекта в Java?] (Http://stackoverflow.com/questions/64036/how-do-you-make-a-deep-copy-of- a-object-in-java) –

ответ

51

Я думаю, что предыдущий зеленый ответ был плохим, почему вы могли бы спросить?

  • Это добавляет много кода
  • Это требует, чтобы вы перечислить все поля, которые необходимо скопировать, и сделать это
  • Это не будет работать для списков при использовании клона() (это то, что клон() для HashMap говорит: возврат неполную копию данного экземпляра HashMap:. ключи и valuesthemselves не клонируются), так что вы в конечном итоге делают это вручную (это заставляет меня плакать)

Ох и кстати сериализации также плохо , вам может потребоваться добавить Serializable по всему месту (это также делает меня cr у).

Так что же такое решение:

Java Глубоководные Клонирование библиотеку Библиотека Клонирование небольшой, с открытым исходным кодом (Apache лицензии) библиотеки Java, какие объекты глубоководной клоны. Объектам не нужно реализовывать интерфейс Cloneable. Эффективно, эта библиотека может клонировать ЛЮБЫЕ java-объекты. Его можно использовать, то есть в реализациях кеша, если вы не хотите, чтобы кешированный объект был изменен или когда вы хотите создать глубокую копию объектов.

Cloner cloner=new Cloner(); 
XX clone = cloner.deepClone(someObjectOfTypeXX); 

Проверьте это в https://github.com/kostaskougios/cloning

+3

Cloner - отличная библиотека (только ее исполнение иногда заставляет меня плакать ... но я думаю, что вы не можете сделать гораздо лучше с отражением) – mik01aj

+0

Это решение выглядит лучше. Не пытайтесь, но есть пара комментариев в этой теме, которые очень положительно относятся к клонированию. – Juraj

+1

Не работает на Android ... – wieczorek1990

2

Эта статья может решить вашу проблему: http://www.matthicks.com/2008/05/fastest-deep-cloning.html

Они используют сериализацию и отражения ,

+0

http://javatechniques.com/blog/faster-deep-copies-of-java-objects/ (это первая ссылка на страницу, но есть исходный код!) – Peter

9

Один общий способ глубокого клонирования произвольной коллекции - сериализовать его в поток, а затем прочитать его в новой коллекции. Вы будете перерегистировать совершенно новые объекты, которые не имеют отношения к старым, кроме идентичных копий.

Заканчивать ответ Бруно для ссылки на Apache Commons serialization utility classes, что будет очень полезно, если это маршрут вы решили взять.

+0

Сериализация решение прекрасно, но я думал о чем-то без него. Я могу гарантировать, что мой пользовательский объект будет правильно глубоко клонирован с помощью метода clone(), но я хочу, чтобы хелпер выполнял его для стандартных классов Java ... – Juraj

+0

Сериализация для клонирования в порядке, но у меня были некоторые поля, которые не являются под моим контролем и которые не являются сериализуемыми ... – Juraj

5

Одним из возможных вариантов является использование сериализации:

Apache Commons предоставляет SerializationUtils

0

Используйте сериализацию, а затем десериализации, но имейте в виду, что этот подход работает только с Serializable классов без переходных полей. Кроме того, ваши синглтоны больше не будут синглонами.

+0

Использование сериализации и десериализации для работы клонирования объектов памяти во время выполнения или между отдельными прогонами программы - плохая идея. Для получения дополнительной информации о том, почему это так, google: «почему сериализация и десериализация плохо». –

18

Все подходы для копирования объектов в Java имеют серьезные недостатки:

Clone

  1. Клон() метод защищен, так что вы не можете назвать это непосредственно, если класс в вопросительных переопределениях это с общедоступным методом.
  2. clone() не вызывает конструктор. Любой конструктор. Он будет выделять память, назначить внутреннее поле class (которое вы можете прочитать через getClass()) и скопировать поля оригинала.

Для более проблем с клоном() см пункт 11 книги Джошуа Блоха "Effective Java, Second Edition"

Serialize

Сериализация еще хуже; он имеет многие из недостатков clone(), а затем некоторые. У Джошуа есть целая глава с четырьмя пунктами только для этой темы.

мое решение

Мое решение добавить новый интерфейс для моих проектов:

public interface Copyable<T> { 
    T copy(); 
    T createForCopy(); 
    void copyTo (T dest); 
} 

код выглядит следующим образом:

class Demo implements Copyable<Demo> { 
    public Demo copy() { 
     Demo copy = createForCopy(); 
     copyTo (copy); 
     return copy; 
    } 
    public Demo createForCopy() { 
     return new Demo(); 
    } 
    public void copyTo (Demo dest) 
     super.copyTo (dest); 
     ...copy fields of Demo here... 
    } 
} 

К сожалению, я должен скопировать этот код ко всем моим объектам, но это всегда один и тот же код, поэтому я могу использовать шаблон редактора Eclipse. Преимущества:

  1. Я могу решить, какой конструктор вызвать и как инициализировать какое поле.
  2. Инициализация происходит в неопределенном порядке (корневой класс для класса экземпляра)
  3. можно повторно использовать существующие объекты и перезаписать их
  4. типобезопасен
  5. Singletons остаться одиночками

Для стандартных типов Java (например, коллекции и т. д.), я использую класс утилиты, который может их скопировать. Методы имеют флаги и обратные вызовы, поэтому я могу контролировать, насколько глубокой должна быть копия.

+2

Я сделал что-то подобное, выполнив clone() во всех классах, которые мне нужно клонировать. Самая большая проблема заключается в том, что если у меня есть коллекция, я должен перебирать ее и копировать ее самостоятельно ... – Juraj

+0

Используйте вспомогательную функцию, которая принимает коллекцию и возвращает ArrayList: поскольку вы знаете размер, который будет выделять память только один раз и ArrayList быстро подходит для обычных видов доступа. –

+0

createForCopy необходимо вернуть демо – TimP

15

неглубоко клонировании коллекция легко, но если вы хотите глубоко клон, библиотека, вероятно, сделать вас лучше, чем руки кодирования его (так как вы хотите, чтобы клонировать элементы внутри сборник как).

Как и у this answer, я использовал Cloner library и, в частности, протестировал его по сравнению с XStream (который может «клонировать» путем сериализации, затем десериализации) и двоичной сериализации. Хотя XStream очень быстро при сериализации в/из XML, Cloner гораздо быстрее клонирования:

0.0851 мс: XStream (клон по сериализации/десериализации)
0,0223 мс: двоичная сериализация (клон по сериализации/десериализации)
0.0017 ms: cloner
* среднее время клонирования простого объекта (два поля) и стандартного конструктора public. Пробег 10000 раз.

В дополнение к быстро, здесь больше причин, чтобы выбрать Cloner:

  1. выполняет глубокий клон какого-либо объекта (даже те, которые вы не пишите сами)
  2. у вас нет чтобы сохранить ваш метод клон() уточненный каждый раз, когда вы добавляете поле
  3. можно клонировать объекты, которые не имеют открытый конструктор по умолчанию
  4. работы с Spring
  5. (оптимизация) Безразлично 't клонировать известные неизменяемые объекты (например, Integer, String и т. д.)
  6. Простой в использовании. Пример:

    cloner.deepClone (anyObject);

+1

славный обзор, спасибо – kostja

11

Я являюсь создателем библиотеки cloner, той, которую представил Брэд. Это решение для клонирования объектов без необходимости писать дополнительный код (нет необходимости в сериализуемых объектах или методе impl clone())

Это довольно быстро, как сказал Брэд, и недавно я загрузил версию, которая еще быстрее. Обратите внимание, что ручное внедрение метода clone() будет быстрее, чем clone lib, но опять же вам нужно будет написать много кода.

Cloner lib работал для меня хорошо, так как я использую его в реализации кеша для сайта с очень интенсивным трафиком (~ 1 млн запросов/день). Кэш должен клонировать приблизительно 10 объектов на запрос. Он достаточно надежный и стабильный. Но, пожалуйста, имейте в виду, что клонирование не без риска. Lib может быть настроен для печати каждого экземпляра класса, который он клонирует во время dev. Таким образом, вы можете проверить, клонирует ли он то, что, по вашему мнению, нужно клонировать, - графы объектов могут быть очень глубокими и содержать ссылки на удивительно большое количество объектов. С помощью clone lib вы можете поручить ему не клонировать объекты, которые вы не хотите, т. Е. Одиночные.

+0

Библиотека выглядит хорошо, проверит ее через некоторое время ... – Juraj

+0

«С клоном lib вы можете поручить ему не клонировать объекты, которые вам не нужны» Я попытался сделать это, но не могло, как я прошу его не клонировать определенные поля? – Sudarshan

+0

Приветствие Константинос. Я использую вашу библиотеку вместо Apache utils (у вас были проблемы с переводами с помощью SerializableUtils). Твой работает без проблем. Отличная работа!. – will824

2

Я использовал эту cloning библиотеку и нашел его весьма полезным. Поскольку у него было несколько ограничений (мне нужен был более тонкий контроль над процессом клонирования: какое поле, в каком контексте и насколько глубоко должно быть клонировано и т. Д.), Я создал расширенную версию. Вы управляете клонированием полей, аннотируя их в классе сущностей.

Просто, чтобы получить вкус его, вот пример класса:

public class CloneMePlease { 
    @Clone(Skip.class) 
    String id3 = UUID.randomUUID().toString(); 

    @Clone(Null.class) 
    String id4 = UUID.randomUUID().toString(); 

    @Clone(value = RandomUUID.class, groups=CustomActivationGroup1.class) 
    String id5 = UUID.randomUUID().toString(); 

    @Clone.List({ 
      @Clone(groups=CustomActivationGroup2.class, value=Skip.class), 
      @Clone(groups=CustomActivationGroup3.class, value=Copy.class)}) 
    Object activationGroupOrderTest = new Object(); 

    @Clone(LongIncrement.class) 
    long version = 1l; 

    @PostClone 
    private void postClone(CloneMePlease original, @CloneInject CloneInjectedService service){ 
     //do stuff with the original source object in the context of the cloned object 
     //you can inject whatewer service you want, from spring/guice to perform custom logic here 
    } 
} 

Подробнее здесь: https://github.com/mnorbi/fluidity-cloning

Существует также зимуют специальное расширение в случае, если один нуждается.

Смежные вопросы