web-dev-qa-db-fra.com

Moyen le plus rapide de comparer deux listes <>

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:

  1. éléments qui apparaissent dans la première liste mais pas dans la seconde
  2. éléments qui apparaissent dans la deuxième liste mais pas dans la première

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?

164
Frank

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();
343
Jon Skeet

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.

32
Rango

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

8
e.gad

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);
}
5
Pius Hermit

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)));
2
Ali Issa

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;
    }
1
Sathish

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.

0

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

0
miguelmpn
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!

0
Devon Parsons