In this article, we will first explore several ways to compare two dictionaries in C#. Then, we will compare and rank the performance of these approaches using the BenchmarkDotNet library. When we work with Dictionary objects in C#, checking their equality is a common task we’re likely to encounter.Â
Without further ado, let’s get started.
Create the Input Dictionaries
Let’s begin by creating two sample dictionary instances:
Dictionary<int, string> dict1 = new() { {1, "Rosary Ogechi"}, {2, "Clare Chiamaka"}, }; Dictionary<int, string> dict2 = new() { {1, "Rosary Ogechi"}, {2, "Clare Chiamaka"}, };
Dictionary
class can be found in our article Dictionary in C#.Compare Two Dictionaries With a Foreach Loop
The first approach we will consider involves iterating through the key-value pairs in our dictionaries:
public static bool UseForeachLoop<TKey, TValue>(Dictionary<TKey, TValue> dict1, Dictionary<TKey, TValue> dict2) where TKey : notnull where TValue : IEquatable<TValue> { if (dict1.Count != dict2.Count) return false; foreach (var kvp in dict1) { if (!dict2.TryGetValue(kvp.Key, out var value) || !value.Equals(kvp.Value)) { return false; } } return true; }
Firstly, we check if we have the same number of items in both dictionaries. If they are not the same, we immediately return false
, indicating that the two dictionaries are not equal. Otherwise, we loop through the pairs and check if the values mapped to a key in both dictionary instances are equal.
If we find any value mismatch, we return false
. However, if the loop completes without finding any mismatch, we return true
, indicating that our dictionaries are equal.
 As you can see, this method is a generic method that accepts two dictionaries with keys that are non-nullable types which we denote with the where TKey : notnull
constraint, and values that are equatable, specified with the where TValue : IEquatable<T>
constraint.Â
Kindly note that all subsequent methods in this article will also be generic methods with the same constraints. We do this to ensure that our methods can work with dictionaries that have non-null keys and equitable values.
Compare Two Dictionaries With the SequenceEqual Method
Alternatively, we can compare two dictionaries in C# by invoking the SequenceEqual()
method:
public static bool UseSequenceEqual<TKey, TValue>(Dictionary<TKey, TValue> dict1, Dictionary<TKey, TValue> dict2) where TKey : notnull, IEquatable<TKey> where TValue : IEquatable<TValue> { if (dict1.Count != dict2.Count) return false; return dict1.OrderBy(kvp => kvp.Key).SequenceEqual(dict2.OrderBy(kvp => kvp.Key)); }
Again, we first check the counts of our dictionary objects and if they are not equal, we return false. If they are equal, we call the OrderBy()
method to convert our dictionaries to IOrderedEnumerable<KeyValuePair<int, string>>
objects and sort them based on their keys.
We do this before invoking the SequenceEqual()
method because if we call it on two dictionaries, or sequences in general, that contain the same items but are unordered, it will return false
.
With the help of the SequenceEqual()
method, we check the equality of our IEnumerable
objects using the default equality comparer for IEnumerable<KeyValuePair<int, string>>
. This default comparer compares our key-value pairs by calling the Equals()
method on both keys and values. So, to ensure our keys are also equatable like our values, we apply the IEquatable<TKey>
constraint to our keys.
With this, if the key-value pairs in both dictionaries are the same, we return true
. Otherwise, we return false
.
Using the Enumerable.All Extension Method
Lastly, let’s use the Enumerable.All()
method to check our dictionaries for equality:
public static bool UseEnumerableAll<TKey, TValue>(Dictionary<TKey, TValue> dict1, Dictionary<TKey, TValue> dict2) where TKey : notnull where TValue : IEquatable<TValue> { if (dict1.Count != dict2.Count) return false; return dict1.All(kvp => dict2.TryGetValue(kvp.Key, out var value) && value.Equals(kvp.Value)); }
Here, we begin by checking if the counts of items in both dictionaries are the same. If they are, we then use the Enumerable.All()
method to go through all the key-value pairs in the first dictionary.
For each pair, we perform two checks. First, we try to retrieve the value corresponding to its key in the second dictionary. Then, if we get a value, we check if it’s the same as the value associated with that same key in the first dictionary.
If all our key-value pairs pass these checks, then, it means our dictionaries are equal, and we return true
. But if any pair fails to meet these conditions, we return false
.
Benchmark to Compare Two Dictionaries
Now, let’s compare the performances of methods with the BenchmarkDotNet library.
To obtain valuable results from our benchmark, we will use two input Dictionary<int, string>
objects, each containing 1000 key-value pairs. To see the benchmark implementation for these methods, you can check the source code for this article.
Now, let’s view our benchmark results:
| Method | Mean | Error | StdDev | Gen0 | Allocated | |----------------- |----------:|---------:|---------:|--------:|----------:| | UseForeachLoop | 12.47 us | 0.022 us | 0.020 us | - | - | | UseEnumerableAll | 26.77 us | 0.024 us | 0.020 us | 0.0305 | 144 B | | UseSequenceEqual | 317.96 us | 0.721 us | 0.639 us | 63.4766 | 200641 B |
From the results, we see that using a loop is the fastest way to compare two dictionaries in C#. This method also shows no memory allocation and no garbage collection.
Following that, we have the Enumerable.All()
method.
Finally, at the bottom of the rankings, we see the SequenceEqual()
method, which runs is about 11x slower than the Enumerable.All()
method. This approach also consumes the largest amount of memory and triggers the garbage collector more often. This happens because, during each execution of this method, we call the OrderBy()
method twice to sort our dictionaries. This call creates and returns two collections, which contributes to the increased memory usage and garbage collector activity.
Conclusion
In this article, we explored different methods for comparing two dictionaries in C#. After defining these methods, we benchmarked them to gain insight into their execution time and memory usage behaviors. It is now left to us as developers to choose the approach that best suits our use case.