Was ist der schnellste (und am wenigsten ressourcenintensive) Vergleich zweier massiver (> 50.000 Elemente) und als Ergebnis zwei Listen wie die folgende:
Derzeit arbeite ich mit List oder IReadOnlyCollection und löse dieses Problem in einer Linq-Abfrage:
var list1 = list.Where(i => !list2.Contains(i)).ToList();
var list2 = list2.Where(i => !list.Contains(i)).ToList();
Aber das ist nicht so gut, wie ich es gerne hätte ... Eine Idee, dies schneller und weniger ressourcenintensiv zu machen, da ich viele Listen bearbeiten muss?
Verwenden Sie Except
:
var firstNotSecond = list1.Except(list2).ToList();
var secondNotFirst = list2.Except(list1).ToList();
Ich vermute, dass es Ansätze gibt, die tatsächlich geringfügig schneller wären als diese, aber selbst dies wird sehr _ schneller als Ihr O (N * M) -Ansatz.
Wenn Sie diese kombinieren möchten, können Sie eine Methode mit den oben genannten und dann eine return-Anweisung erstellen:
return !firstNotSecond.Any() && !secondNotFirst.Any();
Effizienter wäre die Verwendung von Enumerable.Except
:
var inListButNotInList2 = list.Except(list2);
var inList2ButNotInList = list2.Except(list);
Diese Methode wird implementiert, indem die verzögerte Ausführung verwendet wird. Das heißt, Sie könnten zum Beispiel schreiben:
var first10 = inListButNotInList2.Take(10);
Es ist auch effizient, da es intern einen Set<T>
verwendet, um die Objekte zu vergleichen. Dazu werden zunächst alle unterschiedlichen Werte aus der zweiten Sequenz gesammelt und dann die Ergebnisse der ersten Sequenz gestreamt, um zu überprüfen, dass sie noch nicht gesehen wurden.
Wenn Sie möchten, dass die Ergebnisse unabhängig von der GroßKleinschreibung -/sind, funktioniert Folgendes:
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
würde b1.dll enthalten
secondNotFirst
würde b2.dll enthalten
Nicht für dieses Problem, aber hier ist etwas Code, um Listen für gleich und nicht zu vergleichen! identische Objekte:
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);
}
versuchen Sie es so:
var difList = list1.Where(a => !list2.Any(a1 => a1.id == a.id))
.Union(list2.Where(a => !list1.Any(a1 => a1.id == a.id)));
Ich habe diesen Code verwendet, um zwei Listen mit Millionen Datensätzen zu vergleichen.
Diese Methode wird nicht lange dauern
//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;
}
Wenn nur ein kombiniertes Ergebnis benötigt wird, funktioniert dies auch:
var set1 = new HashSet<T>(list1);
var set2 = new HashSet<T>(list2);
var areEqual = set1.SetEquals(set2);
wobei T der Typ des Listenelements ist.
Enumerable.SequenceEqual-Methode
Legt fest, ob zwei Sequenzen gemäß einem Gleichheitsvergleich gleich sind. MS.Docs
Enumerable.SequenceEqual(list1, list2);
Dies funktioniert für alle primitiven Datentypen. Wenn Sie es für benutzerdefinierte Objekte verwenden müssen, müssen Sie IEqualityComparer
implementieren.
Definiert Methoden zur Unterstützung des Vergleichs von Objekten auf Gleichheit.
IEqualityComparer-Schnittstelle
Definiert Methoden zur Unterstützung des Vergleichs von Objekten auf Gleichheit. MS.Docs für 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;
}
}
}
Manchmal müssen Sie nur wissen, dass if zwei Listen unterschiedlich sind und nicht, was diese Unterschiede sind. In diesem Fall sollten Sie diese Erweiterungsmethode Ihrem Projekt hinzufügen. Beachten Sie, dass Ihre aufgelisteten Objekte IEquatable implementieren sollten!
Verwendungszweck:
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
}
Was auch immer die Component
-Klasse ist, die hier für Car
gezeigten Methoden sollten fast identisch implementiert werden.
Es ist sehr wichtig zu wissen, wie wir GetHashCode geschrieben haben. Um IEquatable
ordnungsgemäß zu implementieren, bearbeiten Equals
und GetHashCode
must die Eigenschaften der Instanz logisch kompatibel.
Zwei Listen mit gleichem Inhalt sind immer noch unterschiedliche Objekte und erzeugen unterschiedliche Hash-Codes. Da wir möchten, dass diese beiden Listen als gleich behandelt werden, muss GetHashCode
für jede von ihnen den gleichen Wert erzeugen. Wir können dies erreichen, indem wir den Hashcode an jedes Element in der Liste delegieren und den bitweisen Standard XOR verwenden, um alle zu kombinieren. XOR ist auftragsunabhängig, es ist also egal, ob die Listen anders sortiert sind. Es ist nur wichtig, dass sie nur gleichwertige Mitglieder enthalten.
Hinweis: Der seltsame Name soll bedeuten, dass die Methode die Reihenfolge der Elemente in der Liste nicht berücksichtigt. Wenn Sie sich um die Reihenfolge der Elemente in der Liste kümmern, ist diese Methode nicht für Sie!