Как скопировать HashMap (не мелкой копии) в Java

Мне нужно сделать копию HashMap<Integer, List<MySpecialClass> >, но когда я что-то меняю в копии, я хочу, чтобы оригинал оставался прежним. то есть когда я удаляю что-то из List<MySpecialClass> из копии, он остается в List<MySpecialClass> в оригинале.

Если я правильно понимаю, эти два метода создают только мелкую копию, которая не то, что я хочу:

mapCopy = new HashMap<>(originalMap);
mapCopy = (HashMap) originalMap.clone();

Я прав?

Есть ли лучший способ сделать это, чем просто перебирать все ключи и все элементы списка и скопировать их вручную?

Ответы

Ответ 1

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

В Java нет глубокого копирования для HashMap, поэтому вам все равно придется перебирать все записи и put их в новом HashMap. Но вы также должны делать копию List каждый раз. Что-то вроде этого:

public static HashMap<Integer, List<MySpecialClass>> copy(
    HashMap<Integer, List<MySpecialClass>> original)
{
    HashMap<Integer, List<MySpecialClass>> copy = new HashMap<Integer, List<MySpecialClass>>();
    for (Map.Entry<Integer, List<MySpecialClass>> entry : original.entrySet())
    {
        copy.put(entry.getKey(),
           // Or whatever List implementation you'd like here.
           new ArrayList<MySpecialClass>(entry.getValue()));
    }
    return copy;
}

Если вы хотите изменить свои индивидуальные объекты MySpecialClass и чтобы изменения не отражались в List вашего скопированного HashMap, вам также нужно будет создать новые копии.

Ответ 2

Это, к сожалению, требует итерации. Но это довольно тривиально с потоками Java 8:

mapCopy = map.entrySet().stream()
    .collect(Collectors.toMap(e -> e.getKey(), e -> new ArrayList(e.getValue()));

Ответ 3

Вы делаете копию самого HashMap, поэтому изменение копии HashMap не приведет к изменению исходного HashMap (т.е. добавлению или удалению записей), но поскольку объекты, которые вы сохранили, не являются примитивными типами, список, который вы извлекаете с помощью данная клавиша будет одинаковой, независимо от того, извлекается ли она с первой или второй карты.

Таким образом, есть еще только одна копия этого списка, на которую ссылаются обе карты: изменение списка меняет его независимо от того, какую ссылку вы используете для доступа к ней.

Если вы хотите, чтобы фактический список был отдельной копией, вам нужно будет сделать так, как вы сказали: перейдите по набору записей HashMap и создайте копию каждого списка вручную, добавив его на новую карту по мере продвижения.

Если есть лучший способ, я не знаю, что это такое.

Ответ 4

Вы можете попробовать глубокое клонирование. Посмотрите, например, на https://code.google.com/p/cloning/

Ответ 5

Последовательно сериализовать json и десериализовать:

Map<String, Object> originalMap = new HashMap<>();
String json = new Gson().toJson(originalMap);
Map<String, Object> mapCopy = new Gson().fromJson(
    json, new TypeToken<Map<String, Object>>() {}.getType());

Для специальных классов вам может понадобиться написать пользовательский десериализатор.