2012-07-02 3 views
13

J'ai un hashmap que je veux copier pour un autre usage. Mais chaque fois que je le copie et le réutilise, il change aussi celui d'origine. Pourquoi est-ce?Affectation de Hashmap à Hashmap

do { 
      Map<Integer, Map<String, Object>> map1 = originalMap; 
      //at the second iteration originalMap is the same as map1 of the last iteration, 
      //eventhough the change was nog accepted; 
      //do something with map1 (change value); 
      if(change is accepted) { 
       originalMap = map1; 
      } 
     } while(iteration < 10); 

Merci à l'avance

public static <Integer,String, Schedule>Map<Integer, Map<String, Schedule>> deepCopy(Map<Integer, Map<String, Schedule>> original) { 
    Map<Integer, Map<String, Schedule>> copy = new HashMap<Integer, Map<String, Schedule>>(); 

    for (Map.Entry<Integer, Map<String, Schedule>> entry : original.entrySet()) { 
     copy.put(entry.getKey(), deepCopy2(entry.getValue())); 
    } 
    return copy; 
} 

public static <String, Schedule>Map<String, Schedule> deepCopy2(Map<String, Schedule> original) { 
    Map<String, Schedule> copy = new HashMap<String, Schedule>(); 
    for (Map.Entry<String, Schedule> entry : original.entrySet()) { 
     copy.put(entry.getKey(), entry.getValue()); 
    } 

    return copy; 
} 
+11

car ce n'est pas une copie du 'HashMap' c'est une référence à l'actuel' HashMap', ce qui veut dire que toute modification de l'un affectera l'autre. Vous devez effectuer une copie _deep_ du 'HashMap' –

Répondre

47

Ce que vous avez fait était de ne pas créer une copie de la carte, mais la référence. lorsque deux références pointent vers le même objet, les changements à l'un refléteront dans l'autre.

Solution 1: Si cela était une carte d'un certain type simple à une autre, vous feriez ceci:

Map<SomeType, OtherType> map1 = new HashMap<SomeType, OtherType>(original); 

Ceci est appelé Copy Constructor. Presque Toutes les implémentations standard de Collection et Map en ont une, et c'est généralement la manière la plus simple de cloner une structure simple. Cela fonctionne bien aussi longtemps que SomeType et OtherType sont immutable (par exemple Integer et d'autres types Number, Boolean, String, mais pas des collections, des dates, cartes, tableaux, etc.)

Dans le cas contraire, que d'autres answerers et commentateurs ont souligné, vous devez également copier les valeurs de la carte.

Solution 2: Voici une version rapide et sale qui devrait être en sécurité:

Map<Integer, Map<String, Object>> original=new HashMap<Integer, Map<String,Object>>(); 
Map<Integer, Map<String, Object>> copy = 
     new HashMap<Integer, Map<String, Object>>(); 
for(Entry<Integer, Map<String, Object>> entry : original.entrySet()){ 
    copy.put(entry.getKey(), new HashMap<String, Object>(entry.getValue())); 
} 

Mais en fait, j'aime l'idée de Hunter de fournir une méthode de copie en profondeur. Alors, voici Solution 3: ma propre version en utilisant des paramètres génériques:

public static <K1, K2, V> Map<K1, Map<K2, V>> deepCopy(
    Map<K1, Map<K2, V>> original){ 

    Map<K1, Map<K2, V>> copy = new HashMap<K1, Map<K2, V>>(); 
    for(Entry<K1, Map<K2, V>> entry : original.entrySet()){ 
     copy.put(entry.getKey(), new HashMap<K2, V>(entry.getValue())); 
    } 
    return copy; 
} 

Vous pouvez l'appeler comme ceci:

Map<Integer, Map<String, Object>> original=new HashMap<Integer, Map<String,Object>>(); 
// do stuff here 
Map<Integer, Map<String, Object>> copy = deepCopy(original); 

Mise à jour

J'ai piraté ensemble un classe qui effectue un clonage profond pour Maps, Collections et Arrays (primitif et autre). Utilisation:

Something clone = DeepClone.deepClone(original); 

Ici, il est:

public final class DeepClone { 

    private DeepClone(){} 

    public static <X> X deepClone(final X input) { 
     if (input == null) { 
      return input; 
     } else if (input instanceof Map<?, ?>) { 
      return (X) deepCloneMap((Map<?, ?>) input); 
     } else if (input instanceof Collection<?>) { 
      return (X) deepCloneCollection((Collection<?>) input); 
     } else if (input instanceof Object[]) { 
      return (X) deepCloneObjectArray((Object[]) input); 
     } else if (input.getClass().isArray()) { 
      return (X) clonePrimitiveArray((Object) input); 
     } 

     return input; 
    } 

    private static Object clonePrimitiveArray(final Object input) { 
     final int length = Array.getLength(input); 
     final Object copy = Array.newInstance(input.getClass().getComponentType(), length); 
     // deep clone not necessary, primitives are immutable 
     System.arraycopy(input, 0, copy, 0, length); 
     return copy; 
    } 

    private static <E> E[] deepCloneObjectArray(final E[] input) { 
     final E[] clone = (E[]) Array.newInstance(input.getClass().getComponentType(), input.length); 
     for (int i = 0; i < input.length; i++) { 
      clone[i] = deepClone(input[i]); 
     } 

     return clone; 
    } 

    private static <E> Collection<E> deepCloneCollection(final Collection<E> input) { 
     Collection<E> clone; 
     // this is of course far from comprehensive. extend this as needed 
     if (input instanceof LinkedList<?>) { 
      clone = new LinkedList<E>(); 
     } else if (input instanceof SortedSet<?>) { 
      clone = new TreeSet<E>(); 
     } else if (input instanceof Set) { 
      clone = new HashSet<E>(); 
     } else { 
      clone = new ArrayList<E>(); 
     } 

     for (E item : input) { 
      clone.add(deepClone(item)); 
     } 

     return clone; 
    } 

    private static <K, V> Map<K, V> deepCloneMap(final Map<K, V> map) { 
     Map<K, V> clone; 
     // this is of course far from comprehensive. extend this as needed 
     if (map instanceof LinkedHashMap<?, ?>) { 
      clone = new LinkedHashMap<K, V>(); 
     } else if (map instanceof TreeMap<?, ?>) { 
      clone = new TreeMap<K, V>(); 
     } else { 
      clone = new HashMap<K, V>(); 
     } 

     for (Entry<K, V> entry : map.entrySet()) { 
      clone.put(deepClone(entry.getKey()), deepClone(entry.getValue())); 
     } 

     return clone; 
    } 
} 
+0

se demandant simplement si clone() serait un bon moyen de copier le HashMap? –

+0

@Louis fait. Alexey, je doute que le clone fasse la copie profonde au besoin. –

+2

'clone()' n'est presque jamais une bonne idée (Effective Java item 11), mais il ne va pas faire le travail dans ce cas précis. –

3

En faisant cela:

Map<Integer, Map<String, Object>> copy = originalMap; 

... vous n'êtes pas copie de la carte, ce qui crée seulement une nouvelle variable fait référence à exactement la même carte, et clairement les changements que vous faites en utilisant cette variable seront reflétés dans la carte d'origine - ils pointent vers le même objet en mémoire.Une meilleure copie de la carte d'origine en utilisant le constructor qui reçoit une autre carte comme paramètre:

Map<Integer, Map<String, Object>> copy; 
copy = new HashMap<Integer, Map<String, Object>>(originalMap); 

Le code ci-dessus va créer une copie superficielle de la carte originale, ce qui signifie: si vous modifiez la valeurdes éléments à l'intérieur d'une carte, les changements seront reflétés dans l'autre, mais vous pouvez ajouter/enlever librement les éléments de l'une ou l'autre carte et l'autre ne sera pas affectée. Si ce n'est pas suffisant, vous devrez effectuer une copie en profondeur des éléments de la carte au moment de la copie.

2

Une solution simple et directe serait de boucler un peu plus les valeurs de la carte et de les copier dans une carte:

Map<Integer, Map<String, Object>> map1; 

//iterate over the map copying values into new map 
for(Map.Entry entry : originalMap.entrySet()) 
{ 
    map1.put(entry.getKey(), new HashMap<String, Object>(entry.getValue())); 
} 

serait une meilleure solution pour envelopper cela dans une méthode:

public static <K,J,V> Map<K, Map<J, V>> deepCopy(Map<K, Map<J, V>> original) 
{ 
    Map<K, Map<J, V>> copy; 

    //iterate over the map copying values into new map 
    for(Map.Entry<K, Map<J, V>> entry : original.entrySet()) 
    { 
     copy.put(entry.getKey(), new HashMap<J, V>(entry.getValue())); 
    } 

    return copy; 
} 
+0

Je suis d'accord que la méthode serait une meilleure idée (+1 pour cela), mais je voudrais ajouter des caractères génériques pour le rendre réutilisable (et le rendre statique) –

+0

Modifier était en cours pour le type de sécurité. Il devrait également être statique, bon point –

+0

alors maintenant nous avons tous les deux la même méthode dans notre réponse :-) –

0

Dans votre code, originalMap est simplement une référence à map1. Maintenant, ils pointent tous deux vers les mêmes clés et valeurs. Rappelez-vous, c'est Java où '=' sur les références d'objet est simplement une assignation de référence (pas une copie profonde ou superficielle).

Les collections Java prennent généralement en charge une forme de copie superficielle via clone ou putAll. Dans le cas des cartes, en supposant map1 et map2 sont de type HashMap<KeyType,ValueType>, si vous voulez une carte pour être une copie superficielle d'un autre (ce qui signifie, un objet HashMap distincts mais avec des clés et des valeurs partagées), vous faites ceci:

HashMap<KeyType,ValueType> map1(); 
HashMap<KeyType,ValueType> map2(); 

map2.put(x1,v1); // map2 = {{x1,v1}} 

map1.put(x2,v2); // map1 = {{x2,v2}} 

map1 = map2.clone(); // map1 = {{x1,v1}}, with x2 and v2 gone 

map2.clear(); 
map2.put(x3,v3); // map2 = {{x3,v3}} 
map2.put(x4,v4); // map2 = {{x3,v3},{x4,v4}} 

map1.put(x4,v5); // map1 = {{x1,v1}, {x4,v5}} 

// add all of map2 into map1, replacing any mappings with shared keys 
map1.putAll(map2); // map1 = {{x1,v1},{x3,v3},{x4,v4}}, notice how v5 is gone 

Dans une pensée de départ, vous devez prendre l'habitude de regarder l'API Java. Cela t'aidera beaucoup.

http://docs.oracle.com/javase/6/docs/api/java/util/HashMap.html

0

Cela vient peut-être un peu tard, mais une autre solution simple sera de sérialiser la carte à une sortie Stream et désérialiser à une nouvelle carte objet. C'est aussi l'un des moyens les plus faciles de briser le modèle singleton.

+1

Oui j'ai vu ça, mais avoir les références au début était assez pratique. Donc, copier avec une copie profonde est très agréable, mais pour l'instant, ça ne marche pas –