Der einfachste Weg, Arrays in C # zu vergleichen


177

Arrays.equals()Ermöglicht in Java den einfachen Vergleich des Inhalts von zwei Basisarrays (Überladungen sind für alle Basistypen verfügbar).

Gibt es so etwas in C #? Gibt es eine "magische" Möglichkeit, den Inhalt von zwei Arrays in C # zu vergleichen?


1
Den Tags wurde '.net' hinzugefügt, da diese Technik in anderen ähnlichen .net-basierten Sprachen verwendet werden kann.
Evan Plaice

3
Beachten Sie für alle, die dies lesen, dass die akzeptierte Antwort SequenceEqual verwendet. SequenceEqual prüft nicht nur, ob sie dieselben Daten enthalten, sondern auch, ob sie dieselben Daten in derselben Reihenfolge enthalten
John Demetriou

Antworten:


258

Sie könnten verwenden Enumerable.SequenceEqual. Dies funktioniert für alle IEnumerable<T>Arrays, nicht nur für Arrays.


Dies funktioniert jedoch nur, wenn sie in derselben Reihenfolge sind
John Demetriou

1
SequenceEqualIn Bezug auf die Leistung ist dies möglicherweise keine gute Wahl, da die aktuelle Implementierung möglicherweise eine der Quellen vollständig auflistet, wenn sie sich nur durch die Länge unterscheiden. Mit Arrays könnten wir zuerst die LengthGleichheit überprüfen , um zu vermeiden, dass Arrays unterschiedlicher Länge aufgelistet werden, um am Ende eine Ausbeute zu erzielen false.
Frédéric

71

Verwendung Enumerable.SequenceEqualin LINQ .

int[] arr1 = new int[] { 1,2,3};
int[] arr2 = new int[] { 3,2,1 };

Console.WriteLine(arr1.SequenceEqual(arr2)); // false
Console.WriteLine(arr1.Reverse().SequenceEqual(arr2)); // true

1
Denken Sie daran, dass dies für Null-Argumente wirft, also stellen Sie sicher, dass Sie nicht davon ausgehen, dassnew int[] {1}.SequenceEquals(null) == false
Sara

30

Auch für Arrays (und Tupel) können Sie neue Schnittstellen aus .NET 4.0 verwenden: IStructuralComparable und IStructuralEquatable . Mit ihnen können Sie die Gleichheit von Arrays nicht nur überprüfen, sondern auch vergleichen.

static class StructuralExtensions
{
    public static bool StructuralEquals<T>(this T a, T b)
        where T : IStructuralEquatable
    {
        return a.Equals(b, StructuralComparisons.StructuralEqualityComparer);
    }

    public static int StructuralCompare<T>(this T a, T b)
        where T : IStructuralComparable
    {
        return a.CompareTo(b, StructuralComparisons.StructuralComparer);
    }
}

{
    var a = new[] { 1, 2, 3 };
    var b = new[] { 1, 2, 3 };
    Console.WriteLine(a.Equals(b)); // False
    Console.WriteLine(a.StructuralEquals(b)); // True
}
{
    var a = new[] { 1, 3, 3 };
    var b = new[] { 1, 2, 3 };
    Console.WriteLine(a.StructuralCompare(b)); // 1
}

Verzeihung, sollte das eine 1 oder eine 0 sein a.StructuralCompare(b)?
Mafu

Bei Arrays mit großen Werttypen tritt bei der Verwendung dieser Arrays ein Leistungseinbruch auf, da bei der aktuellen Implementierung jeder zu vergleichende Wert berücksichtigt wird.
Frédéric

18

Für .NET 4.0 und höher können Sie Elemente in Arrays oder Tupeln mithilfe des StructuralComparisons- Typs vergleichen:

object[] a1 = { "string", 123, true };
object[] a2 = { "string", 123, true };

Console.WriteLine (a1 == a2);        // False (because arrays is reference types)
Console.WriteLine (a1.Equals (a2));  // False (because arrays is reference types)

IStructuralEquatable se1 = a1;
//Next returns True
Console.WriteLine (se1.Equals (a2, StructuralComparisons.StructuralEqualityComparer)); 

Edit: Zu früh gesprochen. Kann ich StructualEqualityCompare mit IStructuralComparable durchführen? Ich möchte CompareTo mit zwei Arrays von Objekten aufrufen, um herauszufinden, welches "zuerst" kommt. Ich habe IStructuralComparable versucht se1 = a1; Console.WriteLine (se1.CompareTo (a2, StructuralComparisons.StructuralEqualityComparer)); Erhalten: Konvertiert nicht von 'System.Collections.IEqualityComparer' in 'System.Collections.IComparer'
Shindigo

1
OK - der richtige Aufruf lautet: IStructuralComparable se1 = a1; Console.WriteLine (se1.CompareTo (a2, StructuralComparisons.StructuralComparer));
Shindigo

14

SequenceEqual wird nur dann true zurückgeben, wenn zwei Bedingungen erfüllt sind.

  1. Sie enthalten die gleichen Elemente.
  2. Die Elemente sind in derselben Reihenfolge.

Wenn Sie nur überprüfen möchten, ob sie unabhängig von ihrer Reihenfolge dieselben Elemente enthalten und Ihr Problem vom Typ ist

Enthält values2 alle in values1 enthaltenen Werte?

Sie können die LINQ-Erweiterungsmethode verwenden Enumerable.Exceptund dann prüfen, ob das Ergebnis einen Wert hat. Hier ist ein Beispiel

int[] values1 = { 1, 2, 3, 4 };
int[] values2 = { 1, 2, 5 };
var result = values1.Except(values2);
if(result.Count()==0)
{
   //They are the same
}
else
{
    //They are different
}

Und auch wenn Sie dies verwenden, erhalten Sie die verschiedenen Elemente automatisch. Zwei Fliegen mit einer Klappe.

Denken Sie daran, wenn Sie Ihren Code so ausführen

var result = values2.Except(values1);

Sie erhalten unterschiedliche Ergebnisse.

In meinem Fall habe ich eine lokale Kopie eines Arrays und möchte überprüfen, ob etwas aus dem ursprünglichen Array entfernt wurde, also verwende ich diese Methode.


2
Arrays, die dieselben Werte in unterschiedlicher Reihenfolge enthalten, sind einfach NICHT GLEICH. Haben Sie das Ding 'Demetriou' == 'uoirtemeD'?
edc65

11

Für Unit-Tests können Sie CollectionAssert.AreEqualanstelle von verwenden Assert.AreEqual.

Es ist wahrscheinlich der einfachste Weg.


11

Wenn Sie nullEingaben ordnungsgemäß verarbeiten und die Reihenfolge der Elemente ignorieren möchten , versuchen Sie die folgende Lösung:

static class Extensions
{
    public static bool ItemsEqual<TSource>(this TSource[] array1, TSource[] array2)
    {
        if (array1 == null && array2 == null)
            return true;
        if (array1 == null || array2 == null)
            return false;
        return array1.Count() == array2.Count() && !array1.Except(array2).Any();
    }
}

Der Testcode sieht folgendermaßen aus:

class Program
{
    static void Main(string[] args)
    {
        int[] a1 = new int[] { 1, 2, 3 };
        int[] a2 = new int[] { 3, 2, 1 };
        int[] a3 = new int[] { 1, 3 };
        int[] a4 = null;
        int[] a5 = null;
        int[] a6 = new int[0];

        Console.WriteLine(a1.ItemsEqual(a2)); // Output: True.
        Console.WriteLine(a2.ItemsEqual(a3)); // Output: False.
        Console.WriteLine(a4.ItemsEqual(a5)); // Output: True. No Exception.
        Console.WriteLine(a4.ItemsEqual(a3)); // Output: False. No Exception.
        Console.WriteLine(a5.ItemsEqual(a6)); // Output: False. No Exception.
    }
}

Das war hilfreich für mich, aber wenn a1 = { 1, 1 }und a2 = { 1, 2 }, dann liefert der erste Test das falsche Ergebnis. Die Rückgabeerklärung sollte lautenreturn array1.Count() == array2.Count() && !array1.Except(array2).Any() && !array2.Except(array1).Any();
Polshgiant

2

Für einige Anwendungen kann besser sein:

string.Join(",", arr1) == string.Join(",", arr2)

2

Diese LINQ-Lösung funktioniert, nicht sicher, wie sie in ihrer Leistung mit SequenceEquals verglichen wird. Es werden jedoch unterschiedliche Array-Längen verarbeitet, und .All wird für das erste Element beendet, das nicht gleich ist, ohne das gesamte Array zu durchlaufen.

private static bool arraysEqual<T>(IList<T> arr1, IList<T> arr2)
        =>
            ReferenceEquals(arr1, arr2) || (
                arr1 != null && arr2 != null &&
                arr1.Count == arr2.Count &&
                arr1.Select((a, i) => arr2[i].Equals(a)).All(i => i)
            );

1

elementweise vergleichen? wie wäre es mit

public void Linq78a()
{
 int[] numbers1 = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
 int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
 bool bb = numbers.Zip(numbers1, (a, b) => (a == b)).Any(p => !p);
 if (!bb) Console.WriteLine("Lists are equal (bb)");
   else Console.WriteLine("Lists are not equal (bb)");
}

Ersetzen Sie die Bedingung (a == b) durch alles, was Sie in a und b vergleichen möchten.

(Dies kombiniert zwei Beispiele aus MSDN-Entwickler-Linq-Beispielen. )


1
Es werden keine Arrays unterschiedlicher Länge (möglicherweise falsch true) und nullArrays (Absturz) verarbeitet.
Frédéric

1

Ich habe das in visuellen Studios gemacht und es hat perfekt funktioniert; Vergleichen von Arrays Index für Index mit Kurzschluss dieses Codes.

private void compareButton_Click(object sender, EventArgs e)
        {
            int[] answer = { 1, 3, 4, 6, 8, 9, 5, 4, 0, 6 };
            int[] exam = { 1, 2, 3, 6, 8, 9, 5, 4, 0, 7 };

            int correctAnswers = 0;
            int wrongAnswers = 0;

            for (int index = 0; index < answer.Length; index++)
            {
                if (answer[index] == exam[index])
                {
                    correctAnswers += 1;
                }
                else
                {
                    wrongAnswers += 1;
                }
            }

            outputLabel.Text = ("The matching numbers are " + correctAnswers +
                "\n" + "The non matching numbers are " + wrongAnswers);
        }

die Ausgabe wird sein; Die übereinstimmenden Nummern sind 7 Die nicht übereinstimmenden Nummern sind 3


2
Es verarbeitet keine Arrays unterschiedlicher Länge (stürzt ab), nullArrays (stürzt auch ab) und es tut etwas anderes als das, was das OP verlangt hat. Er fragte nur nach Gleichheit, ohne zu zählen, wie viele Gegenstände sich unterscheiden oder übereinstimmen.
Frédéric

0

Unter der Annahme, dass Array-Gleichheit bedeutet, dass beide Arrays gleiche Elemente bei gleichen Indizes haben, gibt es die SequenceEqualAntwort und die IStructuralEquatableAntwort .

Aber beide haben leistungsbedingte Nachteile.

SequenceEqual Die aktuelle Implementierung wird keine Verknüpfung herstellen, wenn die Arrays unterschiedliche Längen haben. Daher wird möglicherweise eines davon vollständig aufgelistet und jedes seiner Elemente verglichen.

IStructuralEquatableist nicht generisch und kann zum Boxen jedes verglichenen Wertes führen. Darüber hinaus ist die Verwendung nicht sehr einfach und erfordert bereits die Codierung einiger Hilfsmethoden, die es verbergen.

In Bezug auf die Leistung ist es möglicherweise besser, Folgendes zu verwenden:

bool ArrayEquals<T>(T[] first, T[] second)
{
    if (first == second)
        return true;
    if (first == null || second == null)
        return false;
    if (first.Length != second.Length)
        return false;
    for (var i = 0; i < first.Length; i++)
    {
        if (first[i] != second[i])
            return false;
    }
    return true;
}

Aber das ist natürlich auch keine "magische Art", die Array-Gleichheit zu überprüfen.

Nein, derzeit gibt es Arrays.equals()in .Net kein wirkliches Äquivalent zu Java .


Würden nicht zuerst und zweitens beide null wahr sein? null == null, nicht wahr?
Jesse Williams

Der erste Test gibt true zurück, wenn beide vorliegen null. Worum geht es dir?
Frédéric

if (first [i]! = second [i]) funktioniert nicht mit Generika. Sie müssen if (! First [i] .Equals (second [i])) verwenden.
Jack Griffin vor
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.