J'ai 60k éléments qui doivent être vérifiés contre une liste de recherche 20k. Existe-t-il un objet de collection (comme List
, HashTable
) fournissant une méthode exceptionnellement rapide Contains()
? Ou devrais-je écrire le mien? En d'autres termes, la méthode Contains()
par défaut consiste à analyser chaque élément ou à utiliser un meilleur algorithme de recherche.
foreach (Record item in LargeCollection)
{
if (LookupCollection.Contains(item.Key))
{
// Do something
}
}
Note. La liste de recherche est déjà triée.
Dans le cas le plus général, considérons System.Collections.Generic.HashSet
comme structure par défaut des données "Contient", car l'évaluation de Contains
prend un temps constant.
La réponse réelle à "Quelle est la collection qui peut être recherchée le plus rapidement" dépend de la taille spécifique de vos données, de l'ordre de vos commandes, du coût de hachage et de la fréquence de recherche.
Si vous n'avez pas besoin de commander, essayez HashSet<Record>
(nouveau sur .Net 3.5)
Si vous le faites, utilisez un List<Record>
et appelez BinarySearch
.
Avez-vous envisagé List.BinarySearch(item)
?
Vous avez dit que votre grande collection est déjà triée, cela semble donc être l'occasion idéale? Un hachage serait certainement le plus rapide, mais cela pose ses propres problèmes et nécessite beaucoup plus de temps système pour le stockage.
Vous devriez lire ce blog qui a testé plusieurs types de collections et méthodes pour chacun, en utilisant des techniques à la fois simples et multi-tâches.
Selon les résultats, BinarySearch sur une liste et SortedList étaient les plus performants qui couraient constamment au coude à coude lorsqu'ils cherchaient quelque chose comme "valeur".
Lors de l'utilisation d'une collection qui autorise les "clés", les définitions Dictionnaire, ConcurrentDictionary, Hashset et HashTables ont été globalement meilleures.
Gardez les deux listes x et y dans un ordre trié.
Si x = y, faites votre action, si x <y, avancez x, si y <x, avancez y jusqu'à ce que l'une des listes soit vide.
Le temps d'exécution de cette intersection est proportionnel à min (taille (x), taille (y))
Don't lancer une boucle .Contains (), ceci est proportionnel à x * y, ce qui est bien pire.
S'il est possible de trier vos éléments, il existe un moyen beaucoup plus rapide de procéder, puis d'effectuer des recherches dans une hashtable ou un b-tree. Bien que si vos articles ne sont pas triables, vous ne pouvez pas vraiment les mettre dans un b-tree de toute façon.
Quoi qu'il en soit, si triable peut trier les deux listes, alors il suffit de parcourir la liste de recherche dans l'ordre.
Walk lookup list
While items in check list <= lookup list item
if check list item = lookup list item do something
Move to next lookup list item
Si vous utilisez .Net 3.5, vous pouvez créer un code plus propre en utilisant:
foreach (Record item in LookupCollection.Intersect(LargeCollection))
{
//dostuff
}
Je n'ai pas .Net 3.5 ici et c'est donc non testé. Il s'appuie sur une méthode d'extension. Non pas que LookupCollection.Intersect(LargeCollection)
ne soit probablement pas la même chose que LargeCollection.Intersect(LookupCollection)
... ce dernier est probablement beaucoup plus lent.
Cela suppose que LookupCollection est un HashSet
Si vous ne craignez pas de grincer dans les dernières performances, il est fortement conseillé d'utiliser un hachage ou une recherche binaire. Vos ensembles de données ne sont tout simplement pas assez volumineux pour que ce soit un problème 99% du temps.
Mais si cela ne se produit qu'une fois sur des milliers de fois et que les performances sont essentielles (et qu'il s'avère inacceptable d'utiliser la recherche HashSet/binary), vous pouvez certainement écrire votre propre algorithme qui parcourt les listes triées en faisant des comparaisons au fur et à mesure. Chaque liste serait parcourue au plus une fois et dans les cas pathologiques ne serait pas mauvais (une fois que vous avez emprunté cette voie, vous constaterez probablement que la comparaison, en supposant que ce soit une chaîne ou une autre valeur non intégrale, serait la dépense réelle et cette optimisation serait la prochaine étape).