Reputation:
I am trying to compare two arrays with each other. I tried this code and got the following errors.
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;
}
Why do I get that error? I went for a low-tech solution and did this which works fine, but I need to copy/paste it several times for each type.
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;
}
Upvotes: 64
Views: 119861
Reputation: 8598
For .NET 4.0 and higher, you can compare elements in array or tuples using the StructuralComparisons type:
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));
Upvotes: 13
Reputation: 3424
I know this is an old topic, but I think it is still relevant, and would like to share an implementation of an array comparison method which I feel strikes the right balance between performance and elegance.
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);
}
The idea here is to check for all of the early out conditions first, then fall back on SequenceEqual
. It also avoids doing extra branching and instead relies on boolean short-circuit to avoid unecessary execution. I also feel it looks clean and is easy to understand.
Also, by using ICollection
for the parameters, it will work with more than just arrays.
Upvotes: 0
Reputation: 12093
Recommending SequenceEqual is ok, but thinking that it may ever be faster than usual for(;;) loop is too naive.
Here is the reflected code:
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;
}
As you can see it uses 2 enumerators and fires numerous method calls which seriously slow everything down. Also it doesn't check length at all, so in bad cases it can be ridiculously slower.
Compare moving two iterators with beautiful
if (a1[i] != a2[i])
and you will know what I mean about performance.
It can be used in cases where performance is really not so critical, maybe in unit test code, or in cases of some short list in rarely called methods.
Upvotes: 7
Reputation: 147461
Providing that you have LINQ available and don't care too much about performance, the easiest thing is the following:
var arraysAreEqual = Enumerable.SequenceEqual(a1, a2);
In fact, it's probably worth checking with Reflector or ILSpy what the SequenceEqual
methods actually does, since it may well optimise for the special case of array values anyway!
Upvotes: 165
Reputation: 39
SequenceEqual can be faster. Namely in the case where almost all of the time, both arrays have indeed the same length and are not the same object.
It's still not the same functionality as the OP's function, as it won't silently compare null values.
Upvotes: 3
Reputation: 1064004
"Why do i get that error?" - probably, you don't have "using System.Collections;
" at the top of the file - only "using System.Collections.Generic;
" - however, generics are probably safer - see below:
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;
}
Upvotes: 74