Сравнение массивов в С#
Я пытаюсь сравнить два массива друг с другом. Я пробовал этот код и получил следующие ошибки.
static bool ArraysEqual(Array a1, Array a2)
{
if (a1 == a2)
return true;
if (a1 == null || a2 == null)
return false;
if (a1.Length != a2.Length)
return false;
IList list1 = a1, list2 = a2; //error CS0305: Using the generic type 'System.Collections.Generic.IList<T>' requires '1' type arguments
for (int i = 0; i < a1.Length; i++)
{
if (!Object.Equals(list1[i], list2[i])) //error CS0021: Cannot apply indexing with [] to an expression of type 'IList'(x2)
return false;
}
return true;
}
Почему я получаю эту ошибку? Я пошел на низкотехнологичное решение и сделал это, который отлично работает, но мне нужно скопировать/вставить его несколько раз для каждого типа.
static bool ArraysEqual(byte[] a1, byte[] a2)
{
if (a1 == a2)
return true;
if (a1 == null || a2 == null)
return false;
if (a1.Length != a2.Length)
return false;
for (int i = 0; i < a1.Length; i++)
{
if (a1[i] != a2[i])
return false;
}
return true;
}
Ответы
Ответ 1
Обеспечив доступность LINQ и не заботясь о производительности, самое простое:
var arraysAreEqual = Enumerable.SequenceEqual(a1, a2);
На самом деле, вероятно, стоит проверить с Reflector или ILSpy, что на самом деле делают методы SequenceEqual
, так как он может оптимизировать для особого случая значения массива в любом случае!
Ответ 2
"Почему я получаю эту ошибку?" - возможно, у вас нет "using System.Collections;
" в верхней части файла - только "using System.Collections.Generic;
" - однако, скорее всего, более общие варианты - см. ниже:
static bool ArraysEqual<T>(T[] a1, T[] a2)
{
if (ReferenceEquals(a1,a2))
return true;
if (a1 == null || a2 == null)
return false;
if (a1.Length != a2.Length)
return false;
EqualityComparer<T> comparer = EqualityComparer<T>.Default;
for (int i = 0; i < a1.Length; i++)
{
if (!comparer.Equals(a1[i], a2[i])) return false;
}
return true;
}
Ответ 3
Для .NET 4.0 и выше вы можете сравнивать элементы в массиве или кортежах с помощью Структурных сопоставлений:
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));
Ответ 4
Рекомендация SequenceEqual в порядке, но, полагая, что она может быть быстрее, чем обычно, для цикла (;;) слишком наивна.
Вот отраженный код:
public static bool SequenceEqual<TSource>(this IEnumerable<TSource> first,
IEnumerable<TSource> second, IEqualityComparer<TSource> comparer)
{
if (comparer == null)
{
comparer = EqualityComparer<TSource>.Default;
}
if (first == null)
{
throw Error.ArgumentNull("first");
}
if (second == null)
{
throw Error.ArgumentNull("second");
}
using (IEnumerator<TSource> enumerator = first.GetEnumerator())
using (IEnumerator<TSource> enumerator2 = second.GetEnumerator())
{
while (enumerator.MoveNext())
{
if (!enumerator2.MoveNext() || !comparer.Equals(enumerator.Current, enumerator2.Current))
{
return false;
}
}
if (enumerator2.MoveNext())
{
return false;
}
}
return true;
}
Как вы можете видеть, он использует 2 счетчика и вызывает многочисленные вызовы методов, которые серьезно замедляют работу. Также он не проверяет длину вообще, поэтому в плохих случаях это может быть смехотворно медленнее.
Сравнить движущиеся два итератора с красивыми
if (a1[i] != a2[i])
и вы узнаете, что я имею в виду о производительности.
Он может использоваться в тех случаях, когда производительность действительно не так критична, может быть, в коде unit test или в случае небольшого списка в редко называемых методах.
Ответ 5
SequenceEqual может быть быстрее. А именно в том случае, когда почти все время оба массива имеют одинаковую длину и не являются одним и тем же объектом.
Он по-прежнему не такой же функциональный, как функция OP, поскольку он не будет молча сравнивать нулевые значения.
Ответ 6
Я знаю, что это старая тема, но я думаю, что она по-прежнему актуальна и хотела бы поделиться реализацией метода сравнения массива, который, как мне кажется, соответствует правильному балансу между производительностью и элегантностью.
static bool CollectionEquals<T>(ICollection<T> a, ICollection<T> b, IEqualityComparer<T> comparer = null)
{
return ReferenceEquals(a, b) || a != null && b != null && a.Count == b.Count && a.SequenceEqual(b, comparer);
}
Идея здесь состоит в том, чтобы сначала проверить все ранние условия, а затем вернуться на SequenceEqual
. Он также избегает выполнения дополнительного разветвления и вместо этого полагается на логическое короткое замыкание, чтобы избежать ненужного выполнения. Я также чувствую, что он выглядит чистым и его легко понять.
Кроме того, используя ICollection
для параметров, он будет работать не только с массивами.