Рекомендація щодо глибокого клонування [закрито]


74

Чи існує якась утиліта для глибокого клонування для колекцій Java:

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

ПРИМІТКА: віддайте перевагу якомусь рішенню без використання серіалізації, але з використанням методу Object.clone (). Я можу бути впевнений, що мій спеціальний об'єкт реалізує метод clone () і використовуватиме лише класичні класи Java, які можна клонувати ...



Використання бібліотеки клонування врятувало мені день! github.com/kostaskougios/cloning
Гаурав

Відповіді:


65

Я думаю, що попередня зелена відповідь була поганою , чому ви можете запитати?

  • Це додає багато коду
  • Для цього потрібно перерахувати всі поля для копіювання та зробити це
  • Це не буде працювати для Списків при використанні clone () (Це те, що каже clone () для HashMap: Повертає неглибоку копію цього екземпляра HashMap: ключі та значення самі не клоновані.), Тому ви робите це вручну (це робить я плачу)

О, і, до речі, серіалізація теж погана, можливо, вам доведеться додавати Serializable скрізь (це також змушує мене плакати).

Отже, яке рішення:

Бібліотека глибокого клонування Java Бібліотека клонування - це невелика бібліотека Java з відкритим кодом (ліцензія apache), яка глибоко клонує об’єкти. Об'єкти не повинні реалізовувати інтерфейс Cloneable. Фактично, ця бібліотека може клонувати БУДЬ-ЯКІ об'єкти Java. Його можна використовувати, тобто в реалізаціях кешу, якщо ви не хочете, щоб кешований об'єкт змінювався або коли ви хочете створити глибоку копію об'єктів.

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

Перевірте це на https://github.com/kostaskougios/cloning


4
Клонер - чудова бібліотека (лише її продуктивність іноді змушує мене плакати ... але, мабуть, з роздумами ви не можете зробити набагато краще)
mik01aj

Це рішення виглядає краще. Не пробував, але в цій темі є кілька коментарів, які дуже позитивно ставляться до клонера.
Juraj

1
Не працює на Android ...
wieczorek1990,

якою була попередня зелена відповідь?
DaveFar

1
У цієї бібліотеки є одна велика проблема. Якщо ви використовуєте deppClone () просто так, відбувається занадто багато копіювання !!! Що я маю на увазі, це те, що якщо у вас є "перелік", який оголошує "абстрактний" метод у своєму тілі і змушує кожну константу надати різну реалізацію для цього абстрактного методу, після клонування поля з цього переліку, "==" більше не працює для цих констант !! Що може легко спричинити проблеми ... У бібліотеці є кілька рішень. registerFastCloner () та registerImmutable () можуть бути рішенням, ще не пробували їх ... І я не знаю, чи можна це взагалі вирішити!
Мостафа Зейналі,

20

Усі підходи до копіювання об’єктів у Java мають серйозні недоліки:

Клон

  1. Метод clone () захищений, тому ви не можете викликати його безпосередньо, доки відповідний клас не замінить його загальнодоступним методом.
  2. clone () не викликає конструктор. Будь-який конструктор. Він виділить пам’ять, призначить внутрішнє classполе (яке можна прочитати через getClass()) та скопіює поля оригіналу.

Щоб отримати додаткові відомості про clone (), див. Пункт 11 книги Джошуа Блоха " Ефективна Java, друге видання "

Серіалізувати

Серіалізувати ще гірше; він має багато недоліків, 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. Самотні залишаються одноосібниками

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


2
Я зробив щось подібне, застосувавши clone () у всіх класах, які мені потрібно клонувати. Найбільша проблема полягає в тому, що якщо у мене є колекція, я повинен переглядати її та копіювати сам ...
Юрай

Використовуйте допоміжну функцію, яка приймає колекцію і повертає ArrayList: Оскільки ви знаєте розмір, це виділить пам’ять лише один раз, а ArrayList швидкий для звичайних видів доступу.
Аарон Дігулла,

createForCopy повинен повернути демонстраційний
файл

Чи не могли б ви показати шаблон редактора Eclipse?
Міхал

17

Неглибоке клонування колекції просто, але якщо ви хочете глибоко клонувати, бібліотека, мабуть, зробить вас краще, ніж ручне кодування (оскільки ви також хочете клонувати елементи всередині колекції).

Подібно до цієї відповіді , я використовував бібліотеку Cloner і спеціально тестував її за допомогою XStream (який може "клонувати" шляхом серіалізації, а потім десеріалізації) та бінарної серіалізації. Хоча XStream дуже швидко здійснює серіалізацію до / з xml, Cloner набагато швидше клонує:

0,0851 мс: xstream (клон серіалізацією / десеріалізацією)
0,0223 мс: двійкова серіалізація (клон серіалізацією / десеріалізацією)
0,0017 мс: клонер
* середній час клонування простого об’єкта (два поля) та відсутність за замовчуванням, публічний конструктор. Біжи 10000 разів.

Окрім швидкості, ось ще кілька причин вибрати клонер:

  1. виконує глибокий клон будь-якого предмета (навіть того, що ви не пишете самі)
  2. вам не потрібно постійно оновлювати метод clone () кожного разу, коли ви додаєте поле
  3. Ви можете клонувати об'єкти, які не мають загальнодоступного конструктора
  4. працює з Весною
  5. (оптимізація) не клонує відомі незмінні об’єкти (як Integer, String тощо)
  6. простий у використанні. Приклад:

    cloner.deepClone (anyObject);


14

Я є творцем бібліотеки клонерів, тієї, яку представив Бред. Це рішення для клонування об'єктів без необхідності писати додатковий код (не потрібно серіалізуються об'єкти або метод impl clone ())

Це досить швидко, як сказав Бред, і нещодавно я завантажив версію, яка ще швидша. Зауважте, що вручну реалізація методу clone () буде швидшою, ніж clone lib, але знову ж таки вам доведеться написати багато коду.

Cloner lib працював у мене досить добре, оскільки я використовую його в реалізації кешу для сайту з дуже інтенсивним трафіком (~ 1 млн запитів на день). Кеш повинен клонувати приблизно 10 об’єктів на запит. Він досить надійний і стабільний. Але майте на увазі, що клонування не позбавлене ризику. Lib можна налаштувати на println кожного екземпляра класу, який він клонує під час розробки. Таким чином ви можете перевірити, чи клонує він те, що, на вашу думку, слід клонувати - графіки об’єктів можуть бути дуже глибокими і містити посилання на дивно велику кількість об’єктів. За допомогою clone lib ви можете доручити йому не клонувати об'єкти, які ви не хочете, тобто одиночні.


Бібліотека виглядає добре, перевіримо це трохи пізніше ...
Юрай

«За допомогою clone lib ви можете доручити йому не клонувати об’єкти, які вам не потрібні». Я спробував це зробити, але не зміг, як я можу попросити його не клонувати певні поля?
Сударшан

Привіт Костянтиносу. Я використовую вашу бібліотеку замість утиліти Apache (Мав проблеми з приведеннями до SerializableUtils). Ваш працює без проблем. Чудова робота!.
will824

Я давно використовую ur lib, це справді здорово! tks ур! Д:
Адамс. H

10

Одним із загальних способів глибокого клонування довільної колекції є серіалізація в потік, а потім зчитування в новій колекції. Ви будете регідрувати абсолютно нові об’єкти, які не мають ніякого відношення до старих, крім ідентичних копій.

Перевірте відповідь Бруно на посилання на класи утиліти серіалізації Apache Commons , що буде дуже корисно, якщо цим маршрутом ви вирішите піти.


Рішення для серіалізації - це добре, але я думав про щось без нього. Я можу гарантувати, що мій спеціальний об'єкт буде правильно глибоко клонований методом clone (), але я хочу помічника, який зробить це для стандартних класів Java ...
Юрай

Шлях серіалізації до клонування - це нормально, але у мене були деякі поля, які не перебувають під моїм контролем і які не піддаються серіалізації ...
Juraj


2

Я використав цю бібліотеку клонування і виявив її досить корисною. Оскільки він мав декілька обмежень (мені потрібен був більш чіткий детальний контроль над процесом клонування: яке поле, в якому контексті та наскільки глибоко слід клонувати тощо), я створив його розширену версію. Клонуванням полів ви керуєте, додаючи їх до класу сутності.

Щоб отримати його смак, ось приклад класу:

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/fluinity-cloning

Існує також спеціальне продовження сплячого режиму на випадок, якщо воно потрібне.


0

Використовуйте серіалізацію, а потім десеріалізацію, але майте на увазі, що такий підхід працює лише з серіалізованими класами без перехідних полів. Крім того, ваші одинаки більше не будуть одинаками.


Використання серіалізації та десеріалізації для клонування об'єктів пам'яті під час виконання або між окремими запусками програми - погана ідея. Щоб отримати докладнішу інформацію про те, чому це так, погугніть: "чому серіалізація та десеріалізація погані".
Ерік Лещинський
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.