There are many different ways to compare arrays in C#. In this article, we are going to cover many of them, check some differences between each approach, and inspect a benchmark result for the fastest technique.
Let’s dive in.
Preparing the Environment
Before we start comparing arrays, we should create two arrays and fill them with some elements:
private static readonly int[] _firstArray = new int[] { 10, 9, 3, 8, 7 }; private static readonly int[] _secondArray = new int[] { 10, 9, 3, 8, 7 };
Some of our methods are going to use System.Linq
and System.Collections
namespaces, hance, it is necessary to add these namespaces if we are creating an application using .NET version earlier than 6.0:
using System.Collections; using System.Linq;
Now that everything is ready. Let’s start.
Compare Arrays in C# Using == (Equality Operator)
To start with using the equality operator (==
), we are going to create an EqualityOperator
method:
public bool EqualityOperator(int[] firstArray, int[] secondArray) { return firstArray == secondArray; }
This method is going to receive the two arrays we want to compare as parameters. After checking if they are equal using ==
, it is going to return a bool
indicating the result of this operation.
It is important to mention that, as an array works as a reference type, this approach is not going to compare element by element of the arrays, but, if both arrays reference the same memory address. If both arrays are in the same memory address, we can assume they are the same array instance, however, with different variable names.
Compare Using Loop
In this approach, we are going to create a method with a for
loop to iterate the entire array:
public bool ForLoop(int[] firstArray, int[] secondArray) { if (firstArray.Length != secondArray.Length) return false; for (int i = 0; i < firstArray.Length; i++) { if (firstArray[i] != secondArray[i]) return false; } return true; }
Our method receives two arrays as parameters. Then, it compares if both arrays have the same length. If they have different lengths, they don’t share the same values as well.
After that, we can iterate through the entire array comparing if each index element has the same value in both arrays.
Once we find any different value, we can return false
because we know that the arrays are different and we don’t need to go to the next iteration. However, if we finish the loop and don’t find any different value, we can return true
indicating that the arrays are equal.
Compare Using Enumerable Class
The Enumerable
class provides us with many methods to compare if two arrays are equal. However, it requires some attention on our side.
Enumerable.SequenceEqual
Let’s use Enumerable.SequenceEqual
to compare two arrays:
return Enumerable.SequenceEqual(_firstArray, _secondArray);
This is a built-in method that iterates through the array comparing each element. Once we are using arrays of integers, and we know that int
works as a value type, we don’t need to worry about how it is going to compare the elements.
Enumerable.SequenceEqual Using Arrays of Objects
If we are using arrays of objects, it is necessary to do some changes to compare the values.
First, we are going to create an Article
class to represent the elements of our array:
public class Article : IEqualityComparer<Article> { public string? Title { get; set; } public DateTime LastUpdate { get; set; } public bool Equals(Article? first, Article? second) { return first?.Title == second?.Title && first?.LastUpdate == second?.LastUpdate; } public int GetHashCode(Article obj) { return base.GetHashCode(); } }
Note that our Article
class inherits from IEqualityComparer
and implements Equals
and GetHashCode
methods. The Equals
method needs to compare each property from the first parameter with each property from the second parameter.
In this case, for the GetHashCode
method, we just call the base implementation.
Now we are going to create two Article
arrays and fill them with different instances, but equal data:
var firstArticle = new Article() { Title = "First Article", LastUpdate = new() }; var firstArticleCopy = new Article() { Title = "First Article", LastUpdate = new() }; var secondArticle = new Article() { Title = "Second Article", LastUpdate = new() }; var secondArticleCopy = new Article() { Title = "Second Article", LastUpdate = new() }; var articleArray = new Article[] { firstArticle, secondArticle }; var articleArrayCopy = new Article[] { firstArticleCopy, secondArticleCopy };
Finally, we can execute the Enumerable.SequenceEqual
with our third parameter:
return Enumerable.SequenceEqual(articleArray, articleArrayCopy, new Article());
This third parameter (new Article()
) defines the behavior to compare the values from each position of the array.
Enumerable.ReferenceEquals
Let’s see how to use the Enumerable.RefereceEquals
method:
return ReferenceEquals(_firstArray, _secondArray);
Once the Enumerable
class inherits from the object
class, when we call this method, we are calling object.ReferenceEquals
.
This call is going to return false
if both arrays are not referencing the same memory address.
By default, it works similarly to the equality operator (==
).
Enumerable.Equals
Let’s check how to use this built-in method to compare two arrays:
return Equals(_firstArray, _secondArray);
This method will return true
if both arrays are referencing the same memory address. When we call this method, we are calling object.Equals
.
In this case, there’s no difference between calling one method or another because the main difference between Equals
and ReferenceEquals
is that Equals
is a virtual method, so, we can override it in our classes. However, once we are talking about arrays, we can’t override either of them. That said, both method is going to check for reference equality.
Compare Using AsSpan().SequenceEqual()
To use this approach, let’s create a AsSpanSequenceEqual
method:
public bool AsSpanSequenceEqual(int[] firstArray, int[] secondArray) { return firstArray.AsSpan().SequenceEqual(secondArray); }
First, we create a new instance of Span<int>
, then, similar to the Enumerable.SequenceEqual
, we call the SequenceEqual
method. The difference is that this time we are not using the Enumerable
class, but the MemoryExtension
class.
Once our array contains the same elements in the same order, the method returns true
, otherwise, false
.
Compare Arrays in C# Using IStructuralEquatable
Another way to compare two arrays is using the IStructuralEquatable
interface:
IStructuralEquatable structuralEquatable = firstArray; return structuralEquatable.Equals(secondArray, StructuralComparisons.StructuralEqualityComparer);
First, we need to transform our first array in a IStructuralEquatable
and then, call the Equals
method, sending our second array and StructuralComparisons.StructuralEqualityComparer
as parameters. If the array contains the same values, it is going to return true
, otherwise, false
.
Benchmark Comparison
Let’s implement a benchmark comparison to check which is the fastest way to compare arrays.
For this benchmark, we are going to write a FillElements
method to populate two arrays with 1 million elements:
private void FillElements(int length) { _firstArray = new int[length]; _secondArray = new int[length]; for (int i = 0; i < length; i++) { var value = new Random().Next(0, 1000); _firstArray[i] = value; _secondArray[i] = value; } }
After running this benchmark, we can inspect these results:
| Method | Mean | Error | StdDev | Median | |-------------------------- |--------------------:|------------------:|-------------------:|--------------------:| | EnumerableReferenceEquals | 0.0515 ns | 0.0303 ns | 0.0672 ns | 0.0102 ns | | EqualityOperator | 0.0520 ns | 0.0300 ns | 0.0702 ns | 0.0147 ns | | EnumerableEquals | 2.9548 ns | 0.0990 ns | 0.2215 ns | 2.9540 ns | | AsSpanSequenceEqual | 425,462.1829 ns | 9,919.7472 ns | 13,958.5551 ns | 423,506.7871 ns | | EnumerableSequenceEqual | 441,896.0402 ns | 8,699.9267 ns | 15,687.7625 ns | 438,269.9219 ns | | ForLoop | 631,013.9293 ns | 12,496.0988 ns | 26,630.2053 ns | 631,100.8789 ns | | StructuralEquatable | 158,638,399.4792 ns | 4,576,887.9185 ns | 13,205,368.1315 ns | 157,580,000.0000 ns |
It is good to mention that, the three fastest methods (EnumerableReferenceEquals
, EqualityOperator
, EnumerableEquals
) are checking if the arrays are referencing the same instances and not element by element.
The StructuralEquatable
is the slowest method, and it is taking us more than 158 million nanoseconds (0,15 seconds) to compare one million elements.
Among the methods that are comparing each element individually, AsSpanSequenceEqual
and EnumerableSequenceEqual
are the fastest approaches. Achieving less than 500 thousand nanoseconds.
The manual implementation (ForLoop
) takes us 631 000 nanoseconds (0.631 milliseconds).
Conclusion
In this article, we’ve learned that we have many different, built-in and manual, ways to compare arrays in C#. In some of them, we compare just the reference, others we compare value by value.
With the help of the benchmark, we’ve seen the time each method takes, and it can help us to decide which method to use in different scenarios.