Найпростіший спосіб порівняння масивів у C #


180

У Java Arrays.equals()дозволяє легко порівнювати вміст двох основних масивів (перевантаження доступні для всіх основних типів).

Чи є таке в C #? Чи є якийсь "магічний" спосіб порівняння вмісту двох масивів у C #?


1
Додано ".net" до тегів, оскільки цю техніку можна використовувати в інших подібних мовах .net.
Еван Плейс

3
Для всіх, хто читає це, пам’ятайте, що у прийнятій відповіді використовується SequenceEqual. SequenceEqual не тільки перевіряє, чи містять вони однакові дані, але й якщо вони містять однакові дані в тому ж порядку
John Demetriou

Відповіді:


262

Ви можете використовувати Enumerable.SequenceEqual. Це працює для будь-яких IEnumerable<T>, а не лише масивів.


Це працює лише тоді, коли вони є в тому ж порядку
Джон Деметриу

1
SequenceEqualможе бути не дуже вдалим вибором продуктивності, оскільки його поточна реалізація може повністю перерахувати одне з джерел, якщо вони відрізняються лише довжиною. За допомогою масивів ми могли б перевірити Lengthрівність спочатку, щоб уникнути перерахування масивів різної довжини лише для отримання кінцевих результатів false.
Фредерік

72

Використання Enumerable.SequenceEqualв 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
Майте на увазі, що це спричиняє нульові аргументи, тому не забудьте цього припуститиnew int[] {1}.SequenceEquals(null) == false
сара

30

Також для масивів (та кортежів) ви можете використовувати нові інтерфейси від .NET 4.0: IStructuralComparable та IStructuralEquatable . Використовуючи їх, ви зможете не тільки перевірити рівність масивів, але і порівняти їх.

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
}

Вибачте, це повинен бути 1 або 0 в a.StructuralCompare(b)?
мафу

У масивах типу великих значень є використання продуктивності, оскільки їх поточна реалізація посилається на кожне значення для порівняння.
Фредерік

18

Для .NET 4.0 і новіших версій ви можете порівнювати елементи в масиві або кортежах за допомогою типу StructuralComparisons :

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)); 

Редагувати: говорити занадто рано. Чи можу я зробити StructualEqualityCompare з IStructuralComparable? Я хочу зателефонувати CompareTo з двома масивами об'єктів, щоб дізнатися, який з них приходить "першим". Я спробував IStructuralComparable se1 = a1; Console.WriteLine (se1.CompareTo (a2, StructuralComparisons.StructuralEqualityComparer)); Отримання: не вдається перетворити з "System.Collections.IEqualityComparer" в "System.Collections.IComparer"
шиндиго

1
ОК - правильний виклик: IStructuralComparable se1 = a1; Console.WriteLine (se1.CompareTo (a2, StructuralComparisons.StructuralComparer));
шиндиго

15

SequenceEqual поверне істину лише у випадку, якщо дві умови виконані.

  1. Вони містять однакові елементи.
  2. Елементи в одному порядку.

Якщо ви хочете лише перевірити, чи містять вони однакові елементи незалежно від їхнього порядку, і ваша проблема є типовою

Чи містять значення2 всі значення, що містяться у значеннях1?

ви можете скористатися методом розширення LINQ, Enumerable.Exceptа потім перевірити, чи має результат якесь значення. Ось приклад

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
}

А також використовуючи це, ви автоматично отримуєте різні предмети. Дві птахи з одним каменем.

Майте на увазі, якщо ви виконайте такий код, як це

var result = values2.Except(values1);

ви отримаєте різні результати.

У моєму випадку я маю локальну копію масиву і хочу перевірити, чи щось було видалено з початкового масиву, тому я використовую цей метод.


2
Масиви, що містять однакові значення в різному порядку, просто НЕ РІВНІ. У вас річ 'Demetriou' == 'uoirtemeD'?
edc65

Це залежить. Якщо ви використовуєте масиви як не упорядковані колекції і вам потрібно перевірити лише, чи містять вони однакові елементи (наприклад, значення з бази даних проти списку конфігурацій), то це найпростіший спосіб, який я знайшов. Якщо порядок має значення (наприклад, рядок), ви б використовували SequenceEqual.
Армандо

11

Для одиничних тестів можна використовувати CollectionAssert.AreEqualзамість Assert.AreEqual.

Це, мабуть, найпростіший спосіб.


11

Якщо ви хочете nullграціозно обробляти матеріали та ігнорувати порядок елементів, спробуйте наступне рішення:

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();
    }
}

Тестовий код виглядає так:

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.
    }
}

Це було корисно для мене, але якщо a1 = { 1, 1 }і a2 = { 1, 2 }, то перший тест повертає неправильний результат. Заява про повернення має бутиreturn array1.Count() == array2.Count() && !array1.Except(array2).Any() && !array2.Except(array1).Any();
Polshgiant


2

Це рішення LINQ працює, не знаючи, як воно порівнюється у продуктивності з SequenceEquals. Але він обробляє різні довжини масиву, і .All вийде на перший елемент, який не дорівнює, без повторення через весь масив.

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

елементарно порівнювати? а як на рахунок

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)");
}

Замініть умову (a == b) будь-чим, що ви хочете порівняти в a і b.

(це поєднує два приклади зразків розробника MSDN Linq )


1
Він не обробляє масиви різної довжини (можуть неправильно видаватись true) та nullмасиви (вийде з ладу).
Фредерік

1

Я робив це у візуальних студіях, і це прекрасно працювало; порівняння індексу масивів за індексом із коротким цим кодом.

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);
        }

вихід буде; Відповідні номери 7 Невідповідні номери 3


2
Він не обробляє масиви різної довжини (буде збій), nullмасиви (теж вийде з ладу), і він робить щось інше, ніж те, про що попросила ОП. Він лише попросив знати рівність, не рахуючи, скільки предметів відрізняються чи відповідають.
Фредерік

0

Якщо припустити рівність масиву означає, що обидва масиви мають рівні елементи при рівних індексах, є SequenceEqualвідповідь і IStructuralEquatableвідповідь .

Але в обох є недоліки, ефективність роботи.

SequenceEqual поточна реалізація не буде ярликом, коли масиви мають різну довжину, і тому він може перерахувати один з них цілком, порівнюючи кожен його елемент.

IStructuralEquatableне є загальним і може спричинити бокс кожного порівняного значення. Більше того, використовувати це не дуже просто і вже вимагає кодування деяких допоміжних методів, які приховують його.

Можливо, краще, ефективніше, використовувати щось на кшталт:

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

Але звичайно, це не є якимось "магічним способом" перевірки рівності масиву.

Отже, ні, насправді немає еквіваленту Java Arrays.equals()в .Net.


Чи не перше, так і друге, як недійсне, це істинне? null == null, чи не так?
Джессі Вільямс

1
Перший тест повернеться істинним, якщо є обидва null. Який твій погляд?
Фредерік

1
якщо (перший [i]! = другий [i]) не працюватиме з дженериками. Ви повинні використовувати if (! Перше [i] .Equals (друге [i])).
Джек Гриффін
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.