Quel est le moyen le plus rapide (et le moins gourmand en ressources) de comparer deux énormes (> 50 000 éléments) et d’avoir deux listes comme celle ci-dessous:
Je travaille actuellement avec List ou IReadOnlyCollection et je résous ce problème dans une requête linq:
var list1 = list.Where(i => !list2.Contains(i)).ToList();
var list2 = list2.Where(i => !list.Contains(i)).ToList();
Mais cela ne donne pas les résultats escomptés… __. Avez-vous l’idée de rendre cela plus rapide et moins gourmand en ressources, car je dois traiter beaucoup de listes?
Utilisez Except
:
var firstNotSecond = list1.Except(list2).ToList();
var secondNotFirst = list2.Except(list1).ToList();
Je soupçonne que certaines approches pourraient être légèrement plus rapides que cela, mais même cela sera énormément plus rapide que votre approche O (N * M).
Si vous souhaitez les combiner, vous pouvez créer une méthode avec ce qui précède puis une instruction return:
return !firstNotSecond.Any() && !secondNotFirst.Any();
Plus efficace serait d'utiliser Enumerable.Except
:
var inListButNotInList2 = list.Except(list2);
var inList2ButNotInList = list2.Except(list);
Cette méthode est implémentée en utilisant une exécution différée. Cela signifie que vous pourriez écrire par exemple:
var first10 = inListButNotInList2.Take(10);
Il est également efficace car il utilise en interne un Set<T>
pour comparer les objets. Cela fonctionne en collectant d'abord toutes les valeurs distinctes de la deuxième séquence, puis en transmettant les résultats de la première, en vérifiant qu'ils n'ont pas été vus auparavant.
Si vous voulez que les résultats soient insensibles à la casse , ce qui suit fonctionnera:
List<string> list1 = new List<string> { "a.dll", "b1.dll" };
List<string> list2 = new List<string> { "A.dll", "b2.dll" };
var firstNotSecond = list1.Except(list2, StringComparer.OrdinalIgnoreCase).ToList();
var secondNotFirst = list2.Except(list1, StringComparer.OrdinalIgnoreCase).ToList();
firstNotSecond
contient b1.dll
secondNotFirst
contient b2.dll
Pas pour ce problème, mais voici du code pour comparer des listes à égalité et non! objets identiques:
public class EquatableList<T> : List<T>, IEquatable<EquatableList<T>> where T : IEquatable<T>
/// <summary>
/// True, if this contains element with equal property-values
/// </summary>
/// <param name="element">element of Type T</param>
/// <returns>True, if this contains element</returns>
public new Boolean Contains(T element)
{
return this.Any(t => t.Equals(element));
}
/// <summary>
/// True, if list is equal to this
/// </summary>
/// <param name="list">list</param>
/// <returns>True, if instance equals list</returns>
public Boolean Equals(EquatableList<T> list)
{
if (list == null) return false;
return this.All(list.Contains) && list.All(this.Contains);
}
essayez de cette façon:
var difList = list1.Where(a => !list2.Any(a1 => a1.id == a.id))
.Union(list2.Where(a => !list1.Any(a1 => a1.id == a.id)));
J'ai utilisé ce code pour comparer deux listes contenant des millions d'enregistrements.
Cette méthode ne prendra pas beaucoup de temps
//Method to compare two list of string
private List<string> Contains(List<string> list1, List<string> list2)
{
List<string> result = new List<string>();
result.AddRange(list1.Except(list2, StringComparer.OrdinalIgnoreCase));
result.AddRange(list2.Except(list1, StringComparer.OrdinalIgnoreCase));
return result;
}
Si seulement le résultat combiné est nécessaire, cela fonctionnera aussi:
var set1 = new HashSet<T>(list1);
var set2 = new HashSet<T>(list2);
var areEqual = set1.SetEquals(set2);
où T est le type d'élément de liste.
méthode Enumerable.SequenceEqual
Détermine si deux séquences sont égales selon un comparateur d'égalité. MS.Docs
Enumerable.SequenceEqual(list1, list2);
Cela fonctionne pour tous les types de données primitifs. Si vous devez l’utiliser sur des objets personnalisés, vous devez implémenter IEqualityComparer
.
Définit des méthodes pour prendre en charge la comparaison d'objets pour l'égalité.
interface IEqualityComparer
Définit des méthodes pour prendre en charge la comparaison d'objets pour l'égalité. MS.Docs pour IEqualityComparer
using System.Collections.Generic;
using System.Linq;
namespace YourProject.Extensions
{
public static class ListExtensions
{
public static bool SetwiseEquivalentTo<T>(this List<T> list, List<T> other)
where T: IEquatable<T>
{
if (list.Except(other).Any())
return false;
if (other.Except(list).Any())
return false;
return true;
}
}
}
Parfois, il suffit de connaître si deux listes sont différentes, et non quelles sont ces différences. Dans ce cas, envisagez d'ajouter cette méthode d'extension à votre projet. Notez que vos objets listés doivent implémenter IEquatable!
Usage:
public sealed class Car : IEquatable<Car>
{
public Price Price { get; }
public List<Component> Components { get; }
...
public override bool Equals(object obj)
=> obj is Car other && Equals(other);
public bool Equals(Car other)
=> Price == other.Price
&& Components.SetwiseEquivalentTo(other.Components);
public override int GetHashCode()
=> Components.Aggregate(
Price.GetHashCode(),
(code, next) => code ^ next.GetHashCode()); // Bitwise XOR
}
Quelle que soit la classe Component
, les méthodes présentées ici pour Car
doivent être implémentées presque à l'identique.
Il est très important de noter comment nous avons écrit GetHashCode. Afin d'implémenter correctement les variables IEquatable
, Equals
et GetHashCode
must /, elles doivent fonctionner sur les propriétés de l'instance d'une manière compatible du point de vue logique.
Deux listes avec le même contenu sont toujours des objets différents et produiront des codes de hachage différents. Puisque nous voulons que ces deux listes soient traitées de manière égale, nous devons laisser GetHashCode
produire la même valeur pour chacune d’elles. Nous pouvons accomplir cela en déléguant le hashcode à chaque élément de la liste et en utilisant le standard au niveau des bits XOR pour les combiner tous. XOR ne dépend pas de la commande, donc peu importe que les listes soient triées différemment. Il importe seulement qu'ils ne contiennent que des membres équivalents.
Remarque: le nom étrange doit impliquer le fait que la méthode ne considère pas l'ordre des éléments dans la liste. Si vous vous souciez de l'ordre des éléments dans la liste, cette méthode n'est pas pour vous!