La comparaison de deux collections pour l'égalité indépendamment de l'ordre des éléments dans leur
Je voudrais comparer deux collections (en C#), mais je ne suis pas sûr de la meilleure façon de mettre en œuvre efficacement.
J'ai lu l'autre thread sur Énumérable.SequenceEqual, mais ce n'est pas exactement ce que je cherche.
Dans mon cas, deux collections seraient égaux s'ils contiennent les mêmes éléments (peu importe l'ordre).
Exemple:
collection1 = {1, 2, 3, 4};
collection2 = {2, 4, 1, 3};
collection1 == collection2; //true
Ce que je fais habituellement est de parcourir chaque élément d'une collection et de voir si il existe dans le reste de la collection, puis en boucle sur chaque élément de la collection et de voir s'il existe dans la première collection. (Je commence à en comparant les longueurs).
if (collection1.Count != collection2.Count)
return false; //the collections are not equal
foreach (Item item in collection1)
{
if (!collection2.Contains(item))
return false; //the collections are not equal
}
foreach (Item item in collection2)
{
if (!collection1.Contains(item))
return false; //the collections are not equal
}
return true; //the collections are equal
Cependant, ce n'est pas tout à fait correct, et ce n'est probablement pas le moyen le plus efficace de faire comparer deux collections pour l'égalité.
Un exemple je pense que ce serait une erreur est:
collection1 = {1, 2, 3, 3, 4}
collection2 = {1, 2, 2, 3, 4}
Qui serait égal avec ma mise en œuvre. Dois-je simplement compter le nombre de fois que chaque élément est trouvé et assurez-vous que les chiffres sont égaux dans les deux collections?
Les exemples sont en quelque sorte de C# (appelons-le pseudo-C#), mais donner votre réponse dans la langue que vous souhaitez, il n'a pas d'importance.
Remarque: j'ai utilisé des entiers dans les exemples de la simplicité, mais je veux être en mesure d'utiliser une référence de type objets trop (ils ne se comportent pas correctement, comme les clés, parce que la seule référence de l'objet de la comparaison, pas le contenu).
- Comment au sujet de l'algorithme? Toutes les réponses liées par comparer quelque chose, générique listes de comparer linq etc. Vraiment nous n'avons promis à quelqu'un que nous ne seront jamais d'utiliser l'algorithme, comme un vieux façonné programmeur?
- Vous n'êtes pas vérifier pour l'Égalité vous êtes à la vérification de l'Équivalence. C'est pinailleurs, mais une distinction importante. Et il y a longtemps. C'est une bonne Q+R.
- Vous pouvez être intéressé par ce post, qui traite d'une version écoute de la fonction de dictionnaire méthode décrite ci-dessous. Une question avec le plus simple dictionnaire approches est qu'elles ne gèrent pas correctement les valeurs null parce que .NET le Dictionnaire de la classe ne permet pas les clés null.
Vous devez vous connecter pour publier un commentaire.
Il s'avère que Microsoft a déjà couverte, dans son cadre d'essais: CollectionAssert.AreEquivalent
L'aide d'un réflecteur, j'ai modifié le code derrière AreEquivalent() pour créer un correspondant comparateur d'égalité. Il est plus complet que les réponses existantes, car elle prend les valeurs null en compte, met en œuvre IEqualityComparer et a une certaine efficacité et le bord en cas vérifie. de plus, il est Microsoft 🙂
Exemple d'utilisation:
Ou si vous voulez juste pour comparer deux collections directement:
Enfin, vous pouvez utiliser un comparateur d'égalité de votre choix:
new CollectionComparer<int>().Equals(intList1, intList2)
. Il existe également de nombreuses collections qui prennent unIEqualityComparer
comme un ctor paramètre utilisé pour définir la signification de l'égalité dans le champ d'application de cette collection. Par exemple, voir: msdn.microsoft.com/en-us/library/ms132072.aspxEqualityComparer
(soit celui que vous avez fourni ouEqualityComparer.Default
, vous pouvez vérifier Réflecteur ou de la source de référence pour vérifier cela). Vrai, si des objets (et plus précisément thier hashcode changements) alors que cette méthode est en cours d'exécution, puis les résultats sont inattendus, mais cela signifie simplement que cette méthode n'est pas thread-safe dans ce contexte.EqualityComparer
(ouEqualityComparer.Default
si aucun n'est spécifié) et, de nouveau, la mise en œuvre est correcte.public bool AreEquivalent(IEnumerable<T> first, IEnumerable<T> second)
alors il n'y a pas de débat.Equals
en raison de laIEqualityComparer<T>
interface. Ce que vous devriez regarder est le nom de la comparer à lui-même. Dans ce cas, il estMultiSetComparer
qui a du sens.Equals
. Merci.GetHashCode
par Microsoft ici est optimisé pour le taux de collisions et pas la performance deGetHashCode
appeler lui-même (par la commande de l'énumérable dans leGetHashCode
méthode c'est lié à être sur le côté plus lent). Vous devez toujours penser à vos données et de décider par vous-même. Si la logique de commande est plus lente puis justelist.Sum(x => x.GetHashCode())
est bon pour aller (bien que les résultats dans plus de collisions depuis sommation n'est pas un bon code de hachage). Je dis à des tests pour vérifier vos données.GetHashCode
mise en œuvre contient un bug: il va échouer sival
estnull
(c'est à dire si la collection contient unnull
élément).hash = hash * 23 + (val != null ? val.GetHashCode() : 42)
.IEqualityComparer<T>
(voir le dernier exemple). Alternativement, vous pourriez avoir votre classe en œuvreIEquatable<T>
(ou moins de préférenceEquals
etGetHashCode
) de sorte que lorsque le défaut générique comparateur d'égalité est utilisé par le dictionnaire interne (msdn.microsoft.com/en-us/library/x525za90(v=vs. 110).aspx), votre application sera utilisée (msdn.microsoft.com/en-us/library/ms224763(v=vs. 110).aspx).Un simple et assez efficace consiste à trier à la fois les collections et de les comparer pour l'égalité:
Cet algorithme est O(N*logN), tandis que votre solution ci-dessus est O(N^2).
Si les collections ont certaines propriétés, vous pouvez être en mesure de mettre en œuvre une solution plus rapide. Par exemple, si deux de vos collections de hachage sont ensembles, ils ne peuvent pas contenir des doublons. Aussi, afin de vérifier si une table de hachage contient certains éléments est très rapide. Dans ce cas, un algorithme similaire à la vôtre serait susceptible d'être plus rapide.
Créer un Dictionnaire "dict" et ensuite, pour chaque membre de la première collection, ne dict[membre]++;
Puis, en boucle au cours de la deuxième collection de la même manière, mais pour chaque membre de la dict[membre]--.
À la fin, en boucle sur tous les membres dans le dictionnaire:
Edit: aussi loin Que je peux dire c'est du même ordre que le plus efficace algorithme. Cet algorithme est O(N), en supposant que le Dictionnaire utilise O(1) les recherches.
return dict.All(kvp => kvp.Value == 0);
C'est mon (fortement influencé par D. Jennings) générique de mise en œuvre de la méthode de comparaison (en C#):
The keys of a dictionary are compared by reference, so we have to find the original key that is equivalent to the "item"
- ce n'est pas vrai. L'algorithme est basé sur de fausses hypothèses et alors que les travaux, il est terriblement inefficace.Vous pouvez utiliser un Hashset. Regardez les SetEquals méthode.
EDIT: j'ai compris dès que j'ai posé que cela ne fonctionne vraiment que pour les ensembles -- il ne sera pas traiter correctement avec des collections qui ont des éléments en double. Par exemple, { 1, 1, 2 } et { 2, 2, 1 } sera considérée comme l'égale de cet algorithme de point de vue. Si vos collections sont des ensembles (ou de leur égalité peut être mesurée de cette façon), cependant, j'espère que vous trouverez ci-dessous utiles.
La solution que j'utilise est:
Linq le dictionnaire de chose sous les couvertures, c'est aussi O(N). (Remarque, il est O(1) si les collections ne sont pas de la même taille).
J'ai fait un test de cohérence à l'aide de la "SetEqual" méthode proposée par Daniel, le OrderBy/SequenceEquals méthode proposée par Igor, et ma suggestion. Les résultats sont ci-dessous, montrant O(N*LogN) pour Igor et O(N) pour la mine et Daniel.
Je pense que la simplicité de l'Linq se croisent code en fait la meilleure solution.
Dans le cas d'absence de répétitions et aucun ordre, les suivantes EqualityComparer peut être utilisé pour permettre à des collections comme les clés de dictionnaire:
Ici est le ToHashSet() de la mise en œuvre que j'ai utilisé. Le algorithme de code de hachage vient d'être Efficace Java (par voie de Jon Skeet).
ISet<T>
pour exprimer qu'elle est destinée à des ensembles (c'est à dire pas de doublons).ISet
, l'idée ici était de traiter laIEnumerable
comme un ensemble (parce que vous avez unIEnumerable
pour commencer), mais compte tenu de l'0 upvotes dans plus de 5 ans qui ne peuvent pas avoir été la plus forte d'idée 😛Solution .NET 3.5 et le
System.Collections.Generic
espace de noms. Selon Microsoft,SymmetricExceptWith
est un O(n + m) opération, avec n représentant le nombre d'éléments dans le premier set et m représentant le nombre d'éléments dans la seconde. Vous pouvez toujours ajouter un comparateur d'égalité à cette fonction si nécessaire.Si vous utilisez Shouldly, vous pouvez utiliser ShouldAllBe avec Contient.
Et enfin, vous pouvez écrire une extension.
Mise à JOUR
Un paramètre facultatif existe sur ShouldBe méthode.
bool ignoreOrder
sur doit être méthode.Pourquoi ne pas l'utiliser .À l'exception de()
http://msdn.microsoft.com/en-us/library/bb397894.aspx
Except
de ne pas travailler pour le comptage des articles en double. Il sera de retour le cas pour les ensembles {1,2,2} et {1,1,2}.[1, 1, 2] != [1, 2, 2]
. À l'aide deDistinct
serait de les rendre égaux.Un double poste de sortes, mais découvrez ma solution pour comparer des collections. C'est assez simple:
Cela permettra d'effectuer une comparaison d'égalité quel que soit l'ordre:
Il s'agira de vérifier pour voir si les articles ont été ajoutés /supprimés:
Cela permettra de voir quels sont les articles dans le dictionnaire changé:
Post Original ici.
erickson est presque droite: puisque vous voulez match sur le compte des doublons, vous voulez un Sac. En Java, cela ressemble à quelque chose comme:
Je suis sûr que C# est un Ensemble intégré de mise en œuvre. Je voudrais utiliser que la première; si la performance est un problème, vous pouvez toujours utiliser un Ensemble différent de la mise en œuvre, mais d'utiliser la même interface.
Voici ma méthode d'extension variante de ohadsc réponse, dans le cas où c'est utile à quelqu'un
IEnumerable<T>
s sont des requêtes, puis de l'appelCount()
n'est pas une bonne idée. Ohad originale de répondre à l'approche de vérifier s'ils sontICollection<T>
est la meilleure idée.Voici une solution qui est une amélioration par rapport à cette une.
Il y a beaucoup de solutions à ce problème.
Si vous ne vous souciez pas des doublons, vous n'avez pas à trier à la fois. Assurez-vous d'abord qu'ils ont le même nombre d'éléments. Après que sorte l'une de ces collections. Puis binsearch chaque élément de la deuxième collection dans la collection triée. Si vous ne trouvez pas un élément donné d'arrêt et de renvoyer false.
La complexité de cette:
- tri de la première collection: NLog(N)
- la recherche de chaque élément de la deuxième à la première: NLOG(N)
si vous vous retrouvez avec 2*N*LOG(N) en supposant qu'elles correspondent et que vous regardez tout. Ceci est similaire à la complexité du tri à la fois. Aussi cela vous donne l'avantage à arrêter plus tôt si il y a une différence.
Cependant, gardez à l'esprit que si les deux sont triés avant d'étape dans cette comparaison et vous essayez de tri par utiliser quelque chose comme un qsort, le tri sera plus cher. Il y a des optimisations pour cela.
Une autre alternative, ce qui est idéal pour les petites collections où vous savez que la gamme des éléments est d'utiliser un masque de bits d'index. Cela vous donnera un O(n) de la performance.
Une autre alternative est d'utiliser une table de hachage et de le regarder. Pour les petites collections, il est généralement beaucoup mieux de faire le tri ou le masque de bits d'index. Table de hachage ont l'inconvénient de pire localité donc, gardez cela à l'esprit.
De nouveau, c'est que si vous ne vous souciez pas des doublons. Si vous voulez en compte les doublons aller avec tri à la fois.
Dans de nombreux cas, la seule réponse appropriée est celle d'Igor Ostrovsky , d'autres réponses sont basées sur des objets de code de hachage.
Mais lorsque vous générez un code de hachage pour un objet, vous le faites uniquement basé sur son IMMUABLE champs - comme Id de l'objet champ (dans le cas d'une base de données d'entité) -
Pourquoi est-il important de remplacer GetHashCode lorsque la méthode Equals est substituée?
Cela signifie , que si vous comparez deux collections , le résultat pourrait être vrai de la méthode de comparaison, même si les champs des différents éléments ne sont pas égaux .
Profondeur de comparer des collections , vous devez utiliser Igor méthode et de mettre en œuvre IEqualirity .
Lisez les commentaires de moi et monsieur.Schnider est sur ses plus voter post.
James
Permettant de doublons dans les
IEnumerable<T>
(si les jeux ne sont pas souhaitables\possibles) et "ignorant la commande", vous devriez être en mesure d'utiliser un.GroupBy()
.Je ne suis pas un expert sur la complexité des mesures, mais ma compréhension rudimentaire, c'est que ce doit être en O(n). Je comprends O(n^2) comme provenant de l'exécution d'une O(n) fonctionnement à l'intérieur d'un autre O(n) comme
ListA.Where(a => ListB.Contains(a)).ToList()
. Chaque élément de ListB est évalué pour l'égalité contre chaque élément de ListA.Comme je l'ai dit, ma compréhension de la complexité est limitée, donc corrigez-moi si je me trompe.
Cette solution simple les forces de l'
(IEnumerable
's de type générique de mettre en œuvreIComparable
. En raison deOrderBy
définition.Si vous ne voulez pas faire une telle hypothèse, mais encore envie d'utiliser cette solution, vous pouvez utiliser le morceau de code suivant :