Gibt es eine integrierte Methode zum Vergleichen von Sammlungen?
-
09-06-2019 - |
Frage
Ich möchte den Inhalt einiger Sammlungen in meiner Equals-Methode vergleichen.Ich habe ein Wörterbuch und eine IList.Gibt es dafür eine integrierte Methode?
Bearbeitet:Ich möchte zwei Wörterbücher und zwei ILists vergleichen, daher ist meiner Meinung nach klar, was Gleichheit bedeutet: Wenn die beiden Wörterbücher dieselben Schlüssel enthalten, die denselben Werten zugeordnet sind, sind sie gleich.
Lösung
Bestimmt, ob zwei Sequenzen gleich sind, indem ihre Elemente mithilfe eines angegebenen IEqualityComparer(T) verglichen werden.
Sie können die Liste und das Wörterbuch nicht direkt vergleichen, aber Sie können die Werteliste aus dem Wörterbuch mit der Liste vergleichen
Andere Tipps
Wie andere vorgeschlagen und angemerkt haben, SequenceEqual
ist auftragsabhängig.Um dieses Problem zu lösen, können Sie das Wörterbuch nach Schlüssel sortieren (der eindeutig ist und die Sortierung daher immer stabil ist) und es dann verwenden SequenceEqual
.Der folgende Ausdruck prüft, ob zwei Wörterbücher unabhängig von ihrer internen Reihenfolge gleich sind:
dictionary1.OrderBy(kvp => kvp.Key).SequenceEqual(dictionary2.OrderBy(kvp => kvp.Key))
BEARBEITEN: Wie Jeppe Stig Nielsen betonte, haben einige Objekte eine IComparer<T>
das ist mit ihrem unvereinbar IEqualityComparer<T>
, was zu falschen Ergebnissen führt.Wenn Sie Schlüssel mit einem solchen Objekt verwenden, müssen Sie einen korrekten Wert angeben IComparer<T>
für diese Schlüssel.Bei Zeichenfolgenschlüsseln (bei denen dieses Problem auftritt) müssen Sie beispielsweise Folgendes tun, um korrekte Ergebnisse zu erhalten:
dictionary1.OrderBy(kvp => kvp.Key, StringComparer.Ordinal).SequenceEqual(dictionary2.OrderBy(kvp => kvp.Key, StringComparer.Ordinal))
Zusätzlich zu den genannten Sequenzgleich, welche
ist wahr, wenn zwei Listen gleich lang sind und ihre entsprechenden Elemente entsprechend einem Vergleich gleich vergleichen
(Dies kann der Standardvergleicher sein, d. h.eine Überschreibung Equals()
)
Es ist erwähnenswert, dass dies in .Net4 der Fall ist SetEquals An ISet
Objekte
welche
ignoriert die Reihenfolge der Elemente und alle doppelten Elemente.
Wenn Sie also eine Liste von Objekten haben möchten, diese aber nicht in einer bestimmten Reihenfolge vorliegen müssen, sollten Sie dies in Betracht ziehen ISet
(wie ein HashSet
) könnte die richtige Wahl sein.
Werfen Sie einen Blick auf die Enumerable.SequenceEqual Methode
var dictionary = new Dictionary<int, string>() {{1, "a"}, {2, "b"}};
var intList = new List<int> {1, 2};
var stringList = new List<string> {"a", "b"};
var test1 = dictionary.Keys.SequenceEqual(intList);
var test2 = dictionary.Values.SequenceEqual(stringList);
.NET verfügt nicht über leistungsstarke Tools zum Vergleichen von Sammlungen.Ich habe eine einfache Lösung entwickelt, die Sie unter dem folgenden Link finden:
http://robertbouillon.com/2010/04/29/comparing-collections-in-net/
Dadurch wird unabhängig von der Reihenfolge ein Gleichheitsvergleich durchgeführt:
var list1 = new[] { "Bill", "Bob", "Sally" };
var list2 = new[] { "Bob", "Bill", "Sally" };
bool isequal = list1.Compare(list2).IsSame;
Dadurch wird überprüft, ob Elemente hinzugefügt/entfernt wurden:
var list1 = new[] { "Billy", "Bob" };
var list2 = new[] { "Bob", "Sally" };
var diff = list1.Compare(list2);
var onlyinlist1 = diff.Removed; //Billy
var onlyinlist2 = diff.Added; //Sally
var inbothlists = diff.Equal; //Bob
Dadurch wird angezeigt, welche Elemente im Wörterbuch geändert wurden:
var original = new Dictionary<int, string>() { { 1, "a" }, { 2, "b" } };
var changed = new Dictionary<int, string>() { { 1, "aaa" }, { 2, "b" } };
var diff = original.Compare(changed, (x, y) => x.Value == y.Value, (x, y) => x.Value == y.Value);
foreach (var item in diff.Different)
Console.Write("{0} changed to {1}", item.Key.Value, item.Value.Value);
//Will output: a changed to aaa
Ich wusste nichts über die Enumerable.SequenceEqual-Methode (man lernt jeden Tag etwas ...), aber ich wollte die Verwendung einer Erweiterungsmethode vorschlagen;etwas wie das:
public static bool IsEqual(this List<int> InternalList, List<int> ExternalList)
{
if (InternalList.Count != ExternalList.Count)
{
return false;
}
else
{
for (int i = 0; i < InternalList.Count; i++)
{
if (InternalList[i] != ExternalList[i])
return false;
}
}
return true;
}
Interessanterweise sieht es so aus, als hätte Microsoft die von mir beschriebene Funktion für Sie erstellt, nachdem Sie sich zwei Sekunden Zeit genommen haben, um über SequenceEqual zu lesen.
Dies beantwortet Ihre Fragen nicht direkt, wird aber sowohl von den MS-TestTools als auch von NUnit bereitgestellt
CollectionAssert.AreEquivalent
Das macht so ziemlich das, was Sie wollen.
Zum Vergleichen von Sammlungen können Sie auch LINQ verwenden. Enumerable.Intersect
gibt alle Paare zurück, die gleich sind.Sie können zwei Wörterbücher wie folgt vergleichen:
(dict1.Count == dict2.Count) && dict1.Intersect(dict2).Count() == dict1.Count
Der erste Vergleich ist notwendig, weil dict2
kann alle Schlüssel von enthalten dict1
und mehr.
Sie können sich auch Variationen ausdenken, indem Sie verwenden Enumerable.Except
Und Enumerable.Union
die zu ähnlichen Ergebnissen führen.Kann aber verwendet werden, um die genauen Unterschiede zwischen Sätzen zu bestimmen.
Wie wäre es mit diesem Beispiel:
static void Main()
{
// Create a dictionary and add several elements to it.
var dict = new Dictionary<string, int>();
dict.Add("cat", 2);
dict.Add("dog", 3);
dict.Add("x", 4);
// Create another dictionary.
var dict2 = new Dictionary<string, int>();
dict2.Add("cat", 2);
dict2.Add("dog", 3);
dict2.Add("x", 4);
// Test for equality.
bool equal = false;
if (dict.Count == dict2.Count) // Require equal count.
{
equal = true;
foreach (var pair in dict)
{
int value;
if (dict2.TryGetValue(pair.Key, out value))
{
// Require value be equal.
if (value != pair.Value)
{
equal = false;
break;
}
}
else
{
// Require key be present.
equal = false;
break;
}
}
}
Console.WriteLine(equal);
}
Für geordnete Sammlungen (Liste, Array) verwenden SequenceEqual
für HashSet-Nutzung SetEquals
Für das Wörterbuch können Sie Folgendes tun:
namespace System.Collections.Generic {
public static class ExtensionMethods {
public static bool DictionaryEquals<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> d1, IReadOnlyDictionary<TKey, TValue> d2) {
if (object.ReferenceEquals(d1, d2)) return true;
if (d2 is null || d1.Count != d2.Count) return false;
foreach (var (d1key, d1value) in d1) {
if (!d2.TryGetValue(d1key, out TValue d2value)) return false;
if (!d1value.Equals(d2value)) return false;
}
return true;
}
}
}
(Eine optimiertere Lösung verwendet die Sortierung, aber das erfordert IComparable<TValue>
)
NEIN.Der Sammlungsrahmen kennt kein Gleichheitskonzept.Wenn man darüber nachdenkt, gibt es keine Möglichkeit, Sammlungen zu vergleichen, die nicht subjektiv sind.Wenn Sie beispielsweise Ihre IList mit Ihrem Wörterbuch vergleichen, wären sie gleich, wenn alle Schlüssel in der IList wären, alle Werte in der IList wären oder wenn beide in der IList wären?Es gibt keine offensichtliche Möglichkeit, diese beiden Sammlungen zu vergleichen, ohne zu wissen, wofür sie verwendet werden sollen. Daher macht eine allgemeine Gleichheitsmethode keinen Sinn.
Nein, da das Framework nicht weiß, wie es den Inhalt Ihrer Listen vergleicht.
Guck dir das an:
http://blogs.msdn.com/abhinaba/archive/2005/10/11/479537.aspx
public bool CompareStringLists(List<string> list1, List<string> list2)
{
if (list1.Count != list2.Count) return false;
foreach(string item in list1)
{
if (!list2.Contains(item)) return false;
}
return true;
}
Das gab es nicht, gibt es nicht und könnte es auch nicht geben, zumindest würde ich das glauben.Der Grund dafür ist, dass die Sammlungsgleichheit wahrscheinlich ein benutzerdefiniertes Verhalten ist.
Elemente in Sammlungen sollten nicht in einer bestimmten Reihenfolge vorliegen, obwohl sie von Natur aus eine Reihenfolge haben. Darauf sollten sich die Vergleichsalgorithmen nicht verlassen.Angenommen, Sie haben zwei Sammlungen von:
{1, 2, 3, 4}
{4, 3, 2, 1}
Sind sie gleich oder nicht?Sie müssen es wissen, aber ich weiß nicht, was Ihr Standpunkt ist.
Sammlungen sind standardmäßig konzeptionell ungeordnet, bis die Algorithmen die Sortierregeln bereitstellen.Dasselbe, worauf Sie SQL Server aufmerksam machen wird, ist, dass Sie bei der Paginierung Sortierregeln angeben müssen:
Noch zwei weitere Sammlungen:
{1, 2, 3, 4}
{1, 1, 1, 2, 2, 3, 4}
Sind sie wiederum gleich oder nicht?Du sagst es mir ..
Die Elementwiederholbarkeit einer Sammlung spielt in verschiedenen Szenarien eine Rolle, und einige Sammlungen mögen dies Dictionary<TKey, TValue>
Erlauben Sie nicht einmal wiederholte Elemente.
Ich glaube, dass diese Art der Gleichheit anwendungsdefiniert ist und das Framework daher nicht alle möglichen Implementierungen bereitstellt.
Nun, im Allgemeinen Enumerable.SequenceEqual
ist gut genug, gibt aber im folgenden Fall false zurück:
var a = new Dictionary<String, int> { { "2", 2 }, { "1", 1 }, };
var b = new Dictionary<String, int> { { "1", 1 }, { "2", 2 }, };
Debug.Print("{0}", a.SequenceEqual(b)); // false
Ich habe einige Antworten auf Fragen wie diese gelesen (Sie können Google für sie) und was ich im Allgemeinen verwenden würde:
public static class CollectionExtensions {
public static bool Represents<T>(this IEnumerable<T> first, IEnumerable<T> second) {
if(object.ReferenceEquals(first, second)) {
return true;
}
if(first is IOrderedEnumerable<T> && second is IOrderedEnumerable<T>) {
return Enumerable.SequenceEqual(first, second);
}
if(first is ICollection<T> && second is ICollection<T>) {
if(first.Count()!=second.Count()) {
return false;
}
}
first=first.OrderBy(x => x.GetHashCode());
second=second.OrderBy(x => x.GetHashCode());
return CollectionExtensions.Represents(first, second);
}
}
Das bedeutet, dass eine Sammlung die andere in ihren Elementen repräsentiert, auch wiederholt, ohne die ursprüngliche Reihenfolge zu berücksichtigen.Einige Anmerkungen zur Implementierung:
GetHashCode()
dient nur der Ordnung, nicht der Gleichheit;Ich denke, dass es in diesem Fall ausreichtCount()
zählt die Sammlung nicht wirklich auf und fällt direkt in die Eigenschaftsimplementierung vonICollection<T>.Count
Wenn die Referenzen gleich sind, ist es nur Boris