web-dev-qa-db-fra.com

Comment copier HashMap (pas une copie superficielle) dans Java

Je dois faire une copie de HashMap<Integer, List<MySpecialClass> > mais quand je change quelque chose dans la copie, je veux que l'original reste le même. c'est-à-dire lorsque je retire quelque chose du List<MySpecialClass> à partir de la copie, il reste dans le List<MySpecialClass> dans la version originale.

Si je comprends bien, ces deux méthodes créent une copie superficielle qui n'est pas ce que je veux:

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

Ai-je raison?

Existe-t-il une meilleure façon de le faire que de simplement parcourir toutes les clés et tous les éléments de la liste et de les copier manuellement?

33
Mathis

Vous avez raison qu'une copie superficielle ne répondra pas à vos besoins. Il aura des copies des Lists de votre carte d'origine, mais ces Lists feront référence aux mêmes objets List, de sorte qu'une modification à un List d'un HashMap apparaîtra dans le List correspondant de l'autre HashMap.

Aucune copie approfondie n'est fournie pour un HashMap en Java, vous devrez donc parcourir toutes les entrées et put les dans le nouveau HashMap. Mais vous devez également faire une copie du List à chaque fois également. Quelque chose comme ça:

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;
}

Si vous souhaitez modifier vos objets MySpecialClass individuels et que les modifications ne se reflètent pas dans les Lists de vos HashMap copiés, vous devrez en faire de nouvelles copies aussi.

20
rgettman

Cela nécessite malheureusement une itération. Mais c'est assez trivial avec Java 8 flux:

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

Edit: dans les versions ultérieures de Java, e -> List.copyOf(e.getValue()) serait préféré.

27
sprinter

Sérialiser en json et désérialiser ensuite:

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());

Pour les classes spéciales, vous devrez peut-être écrire un désérialiseur personnalisé .

12
neu242

Vous pouvez essayer le clonage en profondeur. Jetez un œil par exemple à https://code.google.com/p/cloning/

2
Nabil Echaouch

Vous créez une copie du HashMap lui-même, donc la modification de la copie du HashMap ne changera pas le HashMap d'origine (c'est-à-dire l'ajout ou la suppression d'entrées), mais parce que les objets que vous avez stockés ne sont pas des types primitifs, la liste que vous récupérez avec une clé donnée sera le même, qu'il soit extrait de la première ou de la deuxième carte.

Ainsi, il n'y a qu'une seule copie de cette liste, référencée par les deux cartes: la modification de la liste la modifie quelle que soit la référence que vous utilisez pour y accéder.

Si vous voulez que la liste réelle soit une copie distincte, vous devrez faire comme vous l'avez dit: parcourir le jeu d'entrées du HashMap et créer une copie de chaque liste manuellement, en l'ajoutant à la nouvelle carte au fur et à mesure.

S'il existe un meilleur moyen que cela, je ne sais pas ce que c'est.

1
Brian