Самый простой способ сравнить массивы в C #


180

В Java Arrays.equals()позволяет легко сравнивать содержимое двух основных массивов (доступны перегрузки для всех основных типов).

Есть ли такая вещь в C #? Есть ли «волшебный» способ сравнения содержимого двух массивов в C #?


1
В теги добавлено «.net», потому что этот метод может быть использован в других похожих языках .net.
Эван Плейс

3
Для всех, кто читает это, имейте в виду, что принятый ответ использует SequenceEqual. SequenceEqual не только проверяет, содержат ли они одинаковые данные, но также и содержат ли они одинаковые данные в одном и том же порядке
Джон Деметриу

Ответы:


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'
shindigo

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

15

SequenceEqual вернет true, только если выполнены два условия.

  1. Они содержат одинаковые элементы.
  2. Элементы в том же порядке.

Если вы только хотите проверить, содержат ли они одинаковые элементы, независимо от их порядка, и ваша проблема имеет тип

Содержит ли values2 все значения, содержащиеся в values1?

Вы можете использовать метод расширения 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

Для некоторых приложений может быть лучше:

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

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.

(это объединяет два примера из примеров Linq разработчика MSDN )


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массивы (также происходит сбой) и делает что-то еще, кроме того, что запросил OP. Он только просил знать равенство, не считая, сколько предметов отличаются или совпадают.
Фредерик

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
Первый тест вернет true, если оба null. Какова ваша позиция?
Фредерик

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