Le moyen le plus efficace pour supprimer plusieurs éléments à partir d'un IList<T>
Quel est le moyen le plus efficace pour supprimer plusieurs éléments à partir d'un IList<T>
objet. Supposons que j'ai un IEnumerable<T>
de tous les articles que je veux supprimer, dans le même ordre d'occurrence dans la liste d'origine.
Le seul moyen que j'ai à l'esprit est:
IList<T> items;
IEnumerable<T> itemsToDelete;
...
foreach (var x in itemsToDelete)
{
items.Remove(x);
}
Mais je suppose que ce n'est pas efficace, parce qu'il doit aller sur la liste depuis le début à chaque fois que la méthode Remove
est appelé.
Avez-vous profilé votre code? combien d'amélioration de la performance avez-vous besoin?
Comme John Skeet dit: Downvoter, des commentaires à ce sujet?
Parce que je travaille avec beaucoup de listes, et je vais avoir à faire à de nombreuses reprises.
L'ordinateur peut généralement faire des choses "de nombreuses fois" très rapidement. Êtes-vous sûr qu'il y est encore un problème de performance?
Est
Comme John Skeet dit: Downvoter, des commentaires à ce sujet?
Parce que je travaille avec beaucoup de listes, et je vais avoir à faire à de nombreuses reprises.
L'ordinateur peut généralement faire des choses "de nombreuses fois" très rapidement. Êtes-vous sûr qu'il y est encore un problème de performance?
Est
items
dans l'ordre? Est itemsToDelete
dans l'ordre? Avez-vous pensé si vous pouvez utiliser une ISet
rapide exclusions? Avez-vous comparé la création d'une nouvelle liste au lieu de modifier un en place avec var set = new HashSet<T>(itemsToDelete); var newList = items.Where(i => !set.Contains(i)).ToList();
? Avez-vous comparé à tous?
OriginalL'auteur Guillermo Gutiérrez | 2013-08-02
Vous devez vous connecter pour publier un commentaire.
Que le nombre d'éléments à supprimer devient plus grand, vous trouverez probablement parcourant la liste et de vérifier chaque élément contre un hashset "des éléments à supprimer" est plus efficace. Une méthode d'extension, comme cela pourrait aider:
Je comparés à l'aide de ce petit programme ci-dessous. (Note qu'il utilise un chemin d'accès optimisé si
IList<T>
est en fait unList<T>
.)Sur ma machine (et l'utilisation de mes données de test), cette extension de la méthode a pris 1,5 secondes pour exécuter vs 17 secondes pour le code dans votre question. Cependant, je n'ai pas testé avec différentes tailles de données. Je suis sûr que pour enlever juste un couple de points
RemoveAll2
sera plus rapide.Mise à JOUR (pour @KarmaEDV & Marque Sowul les commentaires ci-dessous):
Si vous avez besoin d'utiliser un comparateur d'égalité, la méthode d'extension pourrait avoir une surcharge qui prend une telle comparer:
Cette solution est grande, mais la Liste optimisée-Chemin d'accès ne fonctionne pas de manière fiable si la Liste remplace la Méthode contains.
pourriez-vous préciser? List<T>.RemoveAll ne pas remettre en Contient, et vous ne pouvez pas remplacer Contient puisqu'il n'est pas
virtual
.Im pas sûr de ce que tu veux dire par "ne pas remettre en Contient". Oui, désolé, pas de remplacer, mais de surcharge. IEnumerable<T>.Contient(item, customComparer) est une surcharge si vous avez besoin d'un custom comparer. Si vous utilisez que vous devez légèrement adapter la solution proposée.
Si vous comprends, oui, vous avez besoin d'une légère modification dans le cas où vous avez besoin d'utiliser un comparateur. J'ai posté une telle variante dans la réponse.
OriginalL'auteur Eren Ersönmez
Si le
IList<T>
de référence se réfère à une instance deList<T>
, le casting de ce type et à l'aide deRemoveAll
est apte à produire de meilleurs performances que toute autre approche qui ne repose pas sur les détails de sa mise en œuvre.Autrement, alors que l'approche optimale dépendra de la fraction relative des éléments qui vont être supprimés et la nature de la
IList<T>
, je dirais que votre meilleur pari serait de copier leIList<T>
à une nouvelleList<T>
, clairement, et de manière sélective re-ajouter des éléments. Même si les éléments de la liste ne sont pas propices à l'efficacité de hachage, le fait que les éléments de laIEnumerable<T>
sont dans le même ordre que ceux de laIList<T>
rendrait non pertinentes. Commencez par la lecture d'un élément de laIEnumerable<T>
. Ensuite, copiez les éléments du tableau de la liste jusqu'à ce que l'on est trouvé. Ensuite, lisez l'article suivant de laIEnumerable<T>
et copie du tableau de la liste jusqu'à ce que l'on est trouvé, etc. Une fois leIEnumerable<T>
est épuisé, copiez le solde de la matrice de laList<T>
.Cette approche sera rapide avec de nombreuses implémentations de
IList<T>
. Il a un inconvénient majeur: le fait qu'il supprime le et re-ajoute chaque élément peut avoir des effets secondaires indésirables sur des choses comme observables listes. Si une liste peut-être observable, on peut avoir à utiliser un beaucoup plus lent N^2 algorithme pour s'assurer de l'exactitude. [BTW, il ne m'irrite queIList<T>
a unRemove(T)
méthode mais manque un beaucoup plus utileRemoveAll(Func<T,bool>)
méthode. LeRemove(T)
est largement redondante avecIndexOf
etRemoveAt
, tandis queRemoveAll
permettrait O(N) implémentations de nombreuses opérations sont en O(N^2), en son absence, si l'on n'est pas autorisé à supprimer et de rajouter des éléments.OriginalL'auteur supercat
Peut-être cela peut vous aider. D'autres idées du même type pourraient être inclus.
items.Count < itemsToDelete.Count()
plutôt que d'utiliserdouble
s. Aussi, il évite une erreur du compilateur.Je n'ai pas mis l'explication: la division de l'est dans le cas où vous voulez un pourcentage proche: vous pouvez remplacer le 1 pour .9 pour 90%.
OriginalL'auteur celerno