web-dev-qa-db-fra.com

Comparer deux cartes

J'ai deux cartes déclarées comme Map<String, Object>. Le Object ici pourrait être un autre Map<String, Object> (Et ainsi de suite). Je veux vérifier si deux cartes sont exactement les mêmes sans connaître leur profondeur. Au lieu d'utiliser la récursivité, puis-je comparer le résultat de la toString() appelée sur chaque carte? Ou existe-t-il un moyen plus simple de comparer les cartes?

58
noMAD

Réponse rapide

Vous devriez utiliser la méthode equals car elle est implémentée pour effectuer la comparaison souhaitée. toString() utilise lui-même un itérateur, comme equals, mais l'approche est plus inefficace. De plus, comme @Teepeemm l'a souligné, toString est affecté par l'ordre des éléments (essentiellement l'ordre de retour des itérateurs) et ne garantit donc pas la même sortie pour deux cartes différentes (surtout si nous comparons deux cartes différentes).

Note/Warning : Votre question et ma réponse supposent que les classes implémentant l'interface de carte respectent le comportement attendu toString et equals . Les classes par défaut Java le font, mais une classe de carte personnalisée doit être examinée pour vérifier le comportement attendu.

Voir: http://docs.Oracle.com/javase/7/docs/api/Java/util/Map.html

boolean equals(Object o)

Compare l'objet spécifié avec cette carte pour l'égalité. Renvoie true si l'objet donné est également une carte et que les deux cartes représentent les mêmes mappages . Plus formellement, deux mappes m1 et m2 représentent les mêmes mappages si m1.entrySet (). Est égal à (m2.entrySet ()) . Cela garantit que la méthode equals fonctionne correctement dans différentes implémentations de l'interface Map.

Implémentation dans Java Source (Java.util.AbstractMap)

De plus, Java lui-même s'occupe de parcourir tous les éléments et de faire la comparaison afin que vous n'ayez pas à le faire. Regardez l'implémentation de AbstractMap qui est utilisé par les classes comme HashMap:

 // Comparison and hashing

    /**
     * Compares the specified object with this map for equality.  Returns
     * <tt>true</tt> if the given object is also a map and the two maps
     * represent the same mappings.  More formally, two maps <tt>m1</tt> and
     * <tt>m2</tt> represent the same mappings if
     * <tt>m1.entrySet().equals(m2.entrySet())</tt>.  This ensures that the
     * <tt>equals</tt> method works properly across different implementations
     * of the <tt>Map</tt> interface.
     *
     * <p>This implementation first checks if the specified object is this map;
     * if so it returns <tt>true</tt>.  Then, it checks if the specified
     * object is a map whose size is identical to the size of this map; if
     * not, it returns <tt>false</tt>.  If so, it iterates over this map's
     * <tt>entrySet</tt> collection, and checks that the specified map
     * contains each mapping that this map contains.  If the specified map
     * fails to contain such a mapping, <tt>false</tt> is returned.  If the
     * iteration completes, <tt>true</tt> is returned.
     *
     * @param o object to be compared for equality with this map
     * @return <tt>true</tt> if the specified object is equal to this map
     */
    public boolean equals(Object o) {
        if (o == this)
            return true;

        if (!(o instanceof Map))
            return false;
        Map<K,V> m = (Map<K,V>) o;
        if (m.size() != size())
            return false;

        try {
            Iterator<Entry<K,V>> i = entrySet().iterator();
            while (i.hasNext()) {
                Entry<K,V> e = i.next();
                K key = e.getKey();
                V value = e.getValue();
                if (value == null) {
                    if (!(m.get(key)==null && m.containsKey(key)))
                        return false;
                } else {
                    if (!value.equals(m.get(key)))
                        return false;
                }
            }
        } catch (ClassCastException unused) {
            return false;
        } catch (NullPointerException unused) {
            return false;
        }

        return true;
    }

Comparer deux types de cartes différents

toString échoue lamentablement lorsque l'on compare TreeMap et HashMap alors que equals compare correctement le contenu.

Code:

public static void main(String args[]) {
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("2", "whatever2");
map.put("1", "whatever1");
TreeMap<String, Object> map2 = new TreeMap<String, Object>();
map2.put("2", "whatever2");
map2.put("1", "whatever1");

System.out.println("Are maps equal (using equals):" + map.equals(map2));
System.out.println("Are maps equal (using toString().equals()):"
        + map.toString().equals(map2.toString()));

System.out.println("Map1:"+map.toString());
System.out.println("Map2:"+map2.toString());
}

Sortie:

Are maps equal (using equals):true
Are maps equal (using toString().equals()):false
Map1:{2=whatever2, 1=whatever1}
Map2:{1=whatever1, 2=whatever2}
60
Menelaos Bakopoulos

Tant que vous substituez equals() à chaque clé et valeur contenues dans la carte, alors m1.equals(m2) devrait être fiable pour vérifier l'égalité des cartes.

Le même résultat peut également être obtenu en comparant toString() de chaque carte comme vous l'avez suggéré, mais l'utilisation de equals() est une approche plus intuitive.

Ce n'est peut-être pas votre situation spécifique, mais si vous stockez des tableaux dans la carte, cela risque d'être un peu délicat, car ils doivent être comparés valeur par valeur, ou en utilisant Arrays.equals(). Plus de détails à ce sujet voir ici .

11
niculare