In this article, we are going to present you solutions on how to return a default value from a Dictionary in C# and run an evaluation test against these methods to find the most efficient.
Let’s begin.
When Do We Need to Return a Default Value From a Dictionary
One of the most common Dictionary
collections, we utilize in .NET is the Dictionary<TKey, TValue>
class, where TKey
is the type of keys and TValue
is the type of value. An issue that arises frequently is when we try to retrieve an element using a key that does not exist. The program throws a KeyNotFoundException
as expected.
In this article, we will handle the non-existing keys by returning the default value type of the elements.
Using the ContainsKey() Method
First, let’s make use of the common ContainsKey
method:
var myDictionary = new Dictionary<string, int> { { "alice", 1 }, { "bob", 2 }, { "mike", 3 } }; var searchKey = "tom"; Console.WriteLine(myDictionary.ContainsKey(searchKey) ? myDictionary[searchKey] : default);
We construct the simple dictionary myDictionary
instance with a string
type for the keys and an int
type for the values. We assign to the searchKey
variable a key that is not present in our collection. With the use of the null coalescing ?
operator, we check if this key exists in the dictionary. If it does, we return the value. Οtherwise, we return the default value, which in our case is the zero value.
Using the TryGetValue() Method
Another approach is the TryGetValue
method. It is a built-in method of the Dictionary<TKey, TValue>
class in C#, which takes two parameters. The first is the key to look for in the dictionary. The second is an out
parameter to store the value associated with the key. It returns a Boolean
value, that indicates whether the key exists in the collection.
So, let’s perform the same example with the use of the TryGetValue
method:
Console.WriteLine(myDictionary.TryGetValue(searchKey, out var result) ? result : default);
Here, if the key exists in our dictionary, we return the out result
variable, otherwise, we return the default type of values in our collection.
According to the CA1854 performance rule, we should prefer the TryGetValue
method over the ContainsKey
method, when we want to retrieve the associated value of the key too. That is because the myDictionary[searchKey]
Dictionary
indexer access in TryGetValue
extension is already guarded by the ContainsKey
check. That said, in the first example, our code will perform a double look-up in the dictionary, while with the TryGetValue
method it will search in the collection only once.
Using the GetValueOrDefault() Method to Return a Default Value from a Dictionary
In C# 7.1, Microsoft introduces the GetValueOrDefault<TKey, TValue>()
method, a collection extension, which is exactly what we are looking for. When the method is successful, it returns the value associated with the specified key
. When it fails, it returns the defaultValue
.
Let’s use it in our example:
Console.WriteLine(myDictionary.GetValueOrDefault(searchKey));
We simply call the extension with a searchKey
value as the single argument.
Let’s take a deeper look at the source code of the GetValueOrDefault
extension:
TValue GetValueOrDefault(TKey key) { int i = FindEntry(key); if (i >= 0) { return entries[i].value; } return default(TValue); }
In contrast to the TryGetValue
method, the compiled code here omits the step of assigning the value to the out variable as it returns it directly.
Performance Benchmarks
We will evaluate these methods to find the most efficient one in terms of speed, with the benchmark class:
[MemoryDiagnoser] [Orderer(SummaryOrderPolicy.FastestToSlowest)] [RankColumn] public class DefaultValueFromDictionaryInCSharpBenchmark { private Dictionary<string, int> _myDictionary = FillDictionary(); private readonly string _key = "number_1000"; [Benchmark] public int ContainsKey() { return _myDictionary.ContainsKey(_key) ? _myDictionary[_key] : default; } [Benchmark] public int TryGetValue() { return _myDictionary.TryGetValue(_key, out var value) ? value : default; } [Benchmark] public int GetValueOrDefault() { return _myDictionary.GetValueOrDefault(_key); } private static Dictionary<string, int> FillDictionary() { var myDictionary = new Dictionary<string, int>(); for (int i = 1; i < 10000; i++) { myDictionary.Add($"number_{i}", i); } return myDictionary; } }
We create a Dictionary
collection with 10000 records. Then, we assign a value to our search key that exists in our implemented collection. Finally, we have three methods to benchmark our suggested solutions accordingly.
Now, let’s take a look at the results:
| Method | Mean | Error | StdDev | Rank | Allocated | |------------------ |---------:|---------:|---------:|-----:|----------:| | GetValueOrDefault | 23.23 ns | 0.245 ns | 0.217 ns | 1 | - | | TryGetValue | 23.44 ns | 0.726 ns | 2.073 ns | 1 | - | | ContainsKey | 49.27 ns | 1.028 ns | 2.028 ns | 2 | - |
We can see that the GetValueOrDefault
method and the TryGetValue
method produce nearly the same outcome when it comes to efficiency. The GetValueOrDefault
method has a mean time of 23.23 nanoseconds, while the TryGetValue
method has a mean time of 23.44 nanoseconds. We also verify that when we use the ContainsKey
method, our program needs approximately twice the time to return the value we search for, with a mean time of 49.27 nanoseconds.
Now we will run the same benchmark with a search key that does not exist in our dictionary. We assign to our key the following value:
private readonly string key = "number_-1";
Let’s inspect the results:
| Method | Mean | Error | StdDev | Rank | Allocated | |------------------ |---------:|---------:|---------:|-----:|----------:| | TryGetValue | 11.85 ns | 0.223 ns | 0.198 ns | 1 | - | | ContainsKey | 13.03 ns | 0.309 ns | 0.610 ns | 2 | - | | GetValueOrDefault | 14.04 ns | 0.257 ns | 0.343 ns | 3 | - |
We can see that there is a small difference in efficiency between the three methods when we search with a key that is not included in our collection. The TryGetValue
method has a mean time of 11.85 nanoseconds, the ContainsKey
method has a mean time of 13.03 nanoseconds, and the GetValueOrDefault
method has respectively a mean time of 14.04 nanoseconds.
As we expected, the TryGetValue
method and the GetValueOrDefault
method are the fastest extensions for returning a value from a dictionary using an existing search key. Τhe TryGetValue
method appears to be a little more efficient for returning the default type when the key is not present. So when we want to choose between those two, we shall consider the return type and what is more convenient for our program. If we want to return a Boolean
type, then the optimal method is the TryGetValue
extension. If we want to return the value itself, the GetValueOrDefault
method is the option.
Conclusion
In this article, we explored three possible methods to return a default value from a Dictionary collection when our search key is not included. Also, we explored what happens behind the scene for these extensions, and then we evaluated our solutions in terms of efficiency.