TryGetValue Vs ContainsKey With Indexer In .NET

TryGetValue Vs ContainsKey With Indexer Banner Image

Introduction

.NET has TryGetValue as a combination of ContainsKey with Indexer but how do these two methods compare with one another? We're going to look at the use case and when it's best to use them. Also, we're going to look at the speed. They are both very fast but does one edge out another and in which cases? Let's look at the example.

Summary Table

Analysis TypeTryGetValueContainsKey With Indexer
Use CaseLots of retrieve valuesTest if keys exist
PerformanceApproaches O(1)Approaches O(1)
Lines Of Code45

Use Case Code Example

This code works by using the TryGetValue method to get the value from the dictionary by the provided key. The second if statement has a two-step process the ContainsKey method checks on the dictionary and if it is true then use the dictionary indexer to return the value with the provided key.

Dictionary<string, double> mathEquationsConstantsDict = new Dictionary<string, double>()
{
    { "Euler's identity", -1 },
    { "Pythagorean theorem", 5 },
    { "Newton's second law", 9.81 },
    { "Planck's constant", 6.62607015e-34 },
    { "Boltzmann constant", 1.380649e-23 },
    { "Speed of light", 299792458 },
    { "Golden ratio", 1.61803398875 },
    { "Archimedes' principle", 1000 },
    { "Coulomb's law", 8.99e9 },
    { "Ideal gas constant", 8.31446261815324 },
    { "Gaussian integral", 1.77245385091 },
    { "Heisenberg uncertainty principle", 5.272859e-35 },
    { "Avogadro's number", 6.02214076e23 },
    { "Riemann hypothesis", 0.5 },
    { "De Broglie wavelength", 1.227e-10 },
    { "Dirac delta function", double.PositiveInfinity },
    { "Euler's number", 2.71828182846 },
    { "Planck length", 1.616255e-35 },
    { "Catalan's constant", 0.915965594177219 },
    { "Gamma function", 1 }
};

string keyForTryGetValue = "Speed of light";
if(mathEquationsConstantsDict.TryGetValue(keyForTryGetValue, out double mathConstant1))
{
    Console.WriteLine($"TryGetValue:key{keyForTryGetValue}, value:{mathConstant1}");
    //use value in some logic
}

string keyForContainsKey = "Boltzmann constant";
if(mathEquationsConstantsDict.ContainsKey(keyForContainsKey))
{
    double mathConstant2 = mathEquationsConstantsDict[keyForContainsKey];//Get value by using dictionary indexer
    Console.WriteLine($"ContainsKey:key{keyForContainsKey}, value:{mathConstant2}");
    //use value in some logic
}
Code Output
TryGetValue:keySpeed of light, value:299792458
ContainsKey:keyBoltzmann constant, value:1.380649E-23

Performance

Next, we'll compare the performance of both the methods TryGetValue and ContainsKey with Indexer to see how they compare in performance. This test consists of 10 tests that are averaged. Each test has 100 function calls of the test method. Each test method has a list of keys to loop through which is 1 million.

Test Parameters
Test ParametersTotal
Tests10
Function Calls Per Test100
Objects Per Function Call1 Million

TryGetValue Speed Test Code

This code loop through all the keys in the dictionary and calls the TryGetValue method to get the value for an item in the dictionary.

void TestMethod(Dictionary<string, string> dictionary)
{
    foreach (string key in keysList)
    {
        if (dictionary.TryGetValue(key, out string value))
        {
            //use value in some logic
        }
    }
}
Code Output
Test 1:Function Calls:100, In 0m 7s 484ms
Test 2:Function Calls:100, In 0m 7s 307ms
Test 3:Function Calls:100, In 0m 6s 876ms
Test 4:Function Calls:100, In 0m 6s 887ms
Test 5:Function Calls:100, In 0m 6s 993ms
Test 6:Function Calls:100, In 0m 6s 905ms
Test 7:Function Calls:100, In 0m 6s 901ms
Test 8:Function Calls:100, In 0m 6s 929ms
Test 9:Function Calls:100, In 0m 6s 726ms
Test 10:Function Calls:100, In 0m 6s 658ms
TryGetValue Method Average Speed:6967ms, In 10 Tests

We see that this has a baseline of about 7 seconds. We can compare this to the ContainsKey with the indexer method. Next, let's see how ContainsKey compares to the same test.

Full TryGetValue Speed Test Code

using System.Diagnostics;

int numberOfTests = 10;//Number of tests 
int numberOfFunctionCalls = 100;//Number of function calls made per test
int numberOfObjectsToCreate = 1000000;//Number test objects
int lengthOfRandomString = 50;
string testName = "TryGetValue Method";//Test name to print to average
List<string> keysList = new List<string>();
void TestMethod(Dictionary<string, string> dictionary)
{
    foreach (string key in keysList)
    {
        if (dictionary.TryGetValue(key, out string value))
        {
            //use value in some logic
        }
    }
}

List<double> testSpeedList = new List<double>();
for (int testIndex = 0; testIndex < numberOfTests; testIndex++)
{
    testSpeedList.Add(StartTest(testIndex));
}
Console.WriteLine($"{testName} Average Speed:{Math.Round(testSpeedList.Average())}ms, In {numberOfTests} Tests");

double StartTest(int testIndex)
{
    Stopwatch stopwatch = new Stopwatch();
    Dictionary<string, string> testData = GetDictionaryData();//Get intial random generated data
    for (int i = 0; i < numberOfFunctionCalls; i++)
    {
        stopwatch.Start();//Start the Stopwatch timer
        TestMethod(testData);//
        stopwatch.Stop();//Stop the Stopwatch timer
    }
    stopwatch.Stop();//Stop the Stopwatch timer
    Console.WriteLine($"Test {testIndex + 1}:Function Calls:{numberOfFunctionCalls}, In {stopwatch.Elapsed.Minutes}m {stopwatch.Elapsed.Seconds}s {stopwatch.Elapsed.Milliseconds}ms");
    return stopwatch.Elapsed.TotalMilliseconds;
}

Dictionary<string, string> GetDictionaryData()
{
    Dictionary<string, string> testData = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
    keysList = new List<string>();
    for (int i = 0; i < numberOfObjectsToCreate; i++)
    {
        string key = GenerateRandomString(lengthOfRandomString);
        if (!testData.ContainsKey(key))
        {
            string value = GenerateRandomString(lengthOfRandomString);
            testData[key] = value;
            keysList.Add(key);
        }
    }
    return testData;
}
string GenerateRandomString(int length)
{
    Random random = new Random();
    const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    return new string(Enumerable.Repeat(chars, length)
      .Select(s => s[random.Next(s.Length)]).ToArray());
}

ContainsKey With Indexer Speed Test Code

void TestMethod(Dictionary<string, string> dictionary)
{
    foreach (string key in keysList)
    {
        if (dictionary.ContainsKey(key))
        {
            string value = dictionary[key];
            //use value in some logic
        }
    }
}
Code Output
Test 1:Function Calls:100, In 0m 9s 713ms
Test 2:Function Calls:100, In 0m 9s 711ms
Test 3:Function Calls:100, In 0m 9s 763ms
Test 4:Function Calls:100, In 0m 9s 920ms
Test 5:Function Calls:100, In 0m 10s 208ms
Test 6:Function Calls:100, In 0m 10s 16ms
Test 7:Function Calls:100, In 0m 9s 851ms
Test 8:Function Calls:100, In 0m 9s 834ms
Test 9:Function Calls:100, In 0m 10s 299ms
Test 10:Function Calls:100, In 0m 9s 441ms
ContainsKey Method Average Speed:9876ms, In 10 Tests
Full ContainsKey With Indexer Speed Test Code

using System.Diagnostics;

int numberOfTests = 10;//Number of tests 
int numberOfFunctionCalls = 100;//Number of function calls made per test
int numberOfObjectsToCreate = 1000000;//Number test objects
int lengthOfRandomString = 50;
string testName = "ContainsKey Method";//Test name to print to average
List<string> keysList = new List<string>();
void TestMethod(Dictionary<string, string> dictionary)
{
    foreach (string key in keysList)
    {
        if (dictionary.ContainsKey(key))
        {
            string value = dictionary[key];
            //use value in some logic
        }
    }
}

List<double> testSpeedList = new List<double>();
for (int testIndex = 0; testIndex < numberOfTests; testIndex++)
{
    testSpeedList.Add(StartTest(testIndex));
}
Console.WriteLine($"{testName} Average Speed:{Math.Round(testSpeedList.Average())}ms, In {numberOfTests} Tests");

double StartTest(int testIndex)
{
    Stopwatch stopwatch = new Stopwatch();
    Dictionary<string, string> testData = GetDictionaryData();//Get intial random generated data
    for (int i = 0; i < numberOfFunctionCalls; i++)
    {
        stopwatch.Start();//Start the Stopwatch timer
        TestMethod(testData);//
        stopwatch.Stop();//Stop the Stopwatch timer
    }
    stopwatch.Stop();//Stop the Stopwatch timer
    Console.WriteLine($"Test {testIndex + 1}:Function Calls:{numberOfFunctionCalls}, In {stopwatch.Elapsed.Minutes}m {stopwatch.Elapsed.Seconds}s {stopwatch.Elapsed.Milliseconds}ms");
    return stopwatch.Elapsed.TotalMilliseconds;
}

Dictionary<string, string> GetDictionaryData()
{
    Dictionary<string, string> testData = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
    keysList = new List<string>();
    for (int i = 0; i < numberOfObjectsToCreate; i++)
    {
        string key = GenerateRandomString(lengthOfRandomString);
        if (!testData.ContainsKey(key))
        {
            string value = GenerateRandomString(lengthOfRandomString);
            testData[key] = value;
            keysList.Add(key);
        }
    }
    return testData;
}
string GenerateRandomString(int length)
{
    Random random = new Random();
    const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    return new string(Enumerable.Repeat(chars, length)
      .Select(s => s[random.Next(s.Length)]).ToArray());
}

Conclusion

Overall RankMethodSpeed
1TryGetValue6967ms
2ContainsKey With Indexer9876ms

Even though these methods are very fast. TryGetValue has a solid edge over ContainsKey with an Indexer of 2 to 3 seconds improvement. TryGetValue is better in performance and it has fewer lines of code. So if you going to be using the value in any way then you should TryGetValue.

For ContainsKey, you should be using it if you're only doing a check if the key exists and then doing some logic. Don't use it to check if the key is there then get the value as it is slower than TryGetValue.

Know any other ways to compare TryGetValue to ContainsKey with Indexer? Let me know in the comments below.

Get Latest Updates