Неглубокая копия карты на Java

107

Насколько я понимаю, есть несколько способов (возможно, и других) создать мелкую копию a Mapна Java:

Map<String, Object> data = new HashMap<String, Object>();
Map<String, Object> shallowCopy;

// first way
shallowCopy = new HashMap<String, Object>(data);

// second way
shallowCopy = (Map<String, Object>) ((HashMap<String, Object>) data).clone();

Один способ предпочтительнее другого, и если да, то почему?

Стоит упомянуть, что второй способ выдает предупреждение «Unchecked Cast». Поэтому вам нужно добавить, @SuppressWarnings("unchecked")чтобы обойти это, что немного раздражает (см. Ниже).

@SuppressWarnings("unchecked")
public Map<String, Object> getDataAsMap() {
    // return a shallow copy of the data map
    return (Map<String, Object>) ((HashMap<String, Object>) data).clone();
}
DCP
источник
В более новых версиях Java (а точнее, начиная с Java 10) вы можете использовать статический фабричный метод Map.copyOf . Но обратите внимание, что он возвращает неизменяемую карту!
Александр Пирогов 07

Ответы:

106

Всегда лучше копировать с помощью конструктора копирования. clone()в Java не работает (см. SO: Как правильно переопределить метод клонирования? ).

Джош Блох о дизайне - Конструктор копирования против клонирования

Если вы читали статью о клонировании в моей книге, особенно если вы читаете между строк, вы поймете, что, на мой взгляд, cloneона глубоко сломана. [...] Жалко, что Cloneableломается, но бывает.

Блох (который, кстати, разработал и реализовал фреймворк Collection) даже пошел дальше, заявив, что он предоставляет clone()метод только «потому, что люди этого ожидают». Он вообще НЕ рекомендует его использовать.


Я думаю, что более интересными являются споры о том, лучше ли конструктор копирования или фабрика копирования, но это совсем другое обсуждение.

полигенные смазочные материалы
источник
1
Ага, это одна из моих любимых частей книги.
polygenelubricants
1
Я не люблю говорить, что clone () не работает. Я предпочитаю говорить, что клон был ужасным дизайнерским решением и может сильно навредить вам, если вы не используете его должным образом. Кроме того, вы можете никогда не доверять другим методам clone (). Так что мы в итоге похожи, стараемся избегать этого, но это не сломано.
santiagobasulto
4
Разве при использовании copy ctor вам не нужно знать, какую реализацию Map вы копируете? Похоже на ненужное ограничение.
Джон-Хансон
"что он предоставляет метод clone () только" потому что люди этого ожидают "" - источник?
Адам Паркин
60

Ни один из двух: конструктор, на который вы ссылаетесь, определен для реализации HashMap карты (как и для других), но не для самого интерфейса карты (например, рассмотрите реализацию интерфейса карты Provider : вы не найдет этот конструктор).

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

Что касается интерфейса карты (и вашего вопроса, в котором вы спрашиваете, как скопировать карту, а не HashMap), вы должны использовать Map # putAll () :

Копирует все сопоставления с указанной карты на эту карту (дополнительная операция). Эффект от этого вызова эквивалентен вызову put (k, v) на этой карте один раз для каждого отображения ключа k в значение v в указанной карте.

Пример:

// HashMap here, but it works for every implementation of the Map interface
Map<String, Object> data = new HashMap<String, Object>();
Map<String, Object> shallowCopy = new HashMap<String, Object>();

shallowCopy.putAll(data);
Лука Фаджиоли
источник
2
Итак, чтобы уточнить: если вы знаете , что копируете в реализацию, у Mapкоторой есть конструктор копирования, тогда нет причин не использовать конструктор копирования?
Адам Паркин
2
Точно, и вы даже можете подумать об этом иначе: если вы используете, putAllвам не нужно знать, имеет ли Mapиспользуемая вами реализация конструктор копирования или нет. MapТаким образом, простой конструктор копирования любой реализации является избыточным.
Лука Фаджиоли
1
Конечно, хотя обычно я люблю однострочники больше, чем двухстрочные. ;)
Адам Паркин
11

Скопируйте карту, не зная ее реализации:

static final Map shallowCopy(final Map source) throws Exception {
    final Map newMap = source.getClass().newInstance();
    newMap.putAll(source);
    return newMap;
}
Террис
источник
3
Рассмотрите возможность добавления <K,V> параметров типа, чтобы обеспечить безопасность типа.
Barett
1
А что насчет карт без конструкторов без аргументов?
Isaac Saffold