Meilleure façon de comparer deux dictionnaires pour l'égalité
-
29-10-2019 - |
Question
Est-ce la meilleure façon de créer un comparateur pour l'égalité de deux dictionnaires? Cela doit être exact. Notez que Entity.Columns est un dictionnaire de KeyValuepair (String, Object):
public class EntityColumnCompare : IEqualityComparer<Entity>
{
public bool Equals(Entity a, Entity b)
{
var aCol = a.Columns.OrderBy(KeyValuePair => KeyValuePair.Key);
var bCol = b.Columns.OrderBy(KeyValuePAir => KeyValuePAir.Key);
if (aCol.SequenceEqual(bCol))
return true;
else
return false;
}
public int GetHashCode(Entity obj)
{
return obj.Columns.GetHashCode();
}
}
Je ne sais pas non plus sur l'implémentation de GethashCode.
Merci!
La solution
Voici ce que je ferais:
public bool Equals(Entity a, Entity b)
{
if (a.Columns.Count != b.Columns.Count)
return false; // Different number of items
foreach(var kvp in a.Columns)
{
object bValue;
if (!b.Columns.TryGetValue(kvp.Key, out bValue))
return false; // key missing in b
if (!Equals(kvp.Value, bValue))
return false; // value is different
}
return true;
}
De cette façon, vous n'avez pas besoin de commander les entrées (qui est un O (n log n) opération): il vous suffit d'énumérer les entrées dans le premier dictionnaire (Sur)) et essayer de récupérer les valeurs par clé dans le deuxième dictionnaire (O (1)), donc la complexité globale est Sur).
Notez également que votre GetHashCode
La méthode est incorrecte: dans la plupart des cas, il renverra différentes valeurs pour différentes instances de dictionnaire, même si elles ont le même contenu. Et si le code hash est différent, Equals
ne s'appellera jamais ... vous avez plusieurs options pour l'implémenter correctement, aucun d'entre eux idéal:
- Construisez le code de hash à partir du contenu du dictionnaire: serait la meilleure option, mais c'est lent, et
GetHashCode
doit être rapide - Renvoie toujours la même valeur, de cette façon
Equals
sera toujours appelé: très Mauvais si vous souhaitez utiliser ce comparateur dans un hachage / dictionnaire / hashset, car toutes les instances tomberont dans le même seau, entraînant Sur) accès au lieu de O (1) - retourner le
Count
du dictionnaire (comme suggéré par Digemall): il ne donnera pas une grande distribution, mais toujours mieux que de toujours renvoyer la même valeur, et il satisfait la contrainte pourGetHashCode
(IE Les objets considérés comme égaux devraient avoir le même code de hash; deux dictionnaires "égaux" ont le même nombre d'éléments, donc ça marche)
Autres conseils
Quelque chose comme ça vient à l'esprit, mais il pourrait y avoir quelque chose de plus efficace:
public static bool Equals<TKey, TValue>(IDictionary<TKey, TValue> x,
IDictionary<TKey, TValue> y)
{
return x.Keys.Intersect(y.Keys).Count == x.Keys.Count &&
x.Keys.All(key => Object.Equals(x[key], y[key]));
}
Cela me semble bon, peut-être pas le plus rapide mais qui fonctionne.
Vous avez juste besoin de changer le GetHashCode
implémentation qui est erronée.
Par exemple, vous pourriez revenir obj.Columns.Count.GetHashCode()