C#
performance
ContainsKey
TryGetValue
dictionary operations

What is performance of ContainsKey and TryGetValue?

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

Dictionary lookups in C# are usually very fast, but there is still a meaningful difference between ContainsKey and TryGetValue depending on what you need. If you want both existence and the stored value, TryGetValue is normally the better choice because it avoids doing the lookup twice.

What the Two Methods Actually Do

ContainsKey answers one question: does this key exist.

csharp
var exists = dictionary.ContainsKey("user:42");

TryGetValue answers two questions at once: does the key exist, and if so, what is the value.

csharp
1if (dictionary.TryGetValue("user:42", out var user))
2{
3    Console.WriteLine(user.Name);
4}

Both methods use the dictionary's hash-based lookup machinery, so in normal conditions both are expected to be close to constant time. The important performance distinction is not that one is O(1) and the other is not. The real difference is whether you perform one lookup or two.

The Double-Lookup Pattern

This is a very common pattern:

csharp
1if (dictionary.ContainsKey(key))
2{
3    var value = dictionary[key];
4    Console.WriteLine(value);
5}

It works, but it often performs redundant work:

  1. ContainsKey searches for the key
  2. the indexer dictionary[key] searches again

That means extra hashing and bucket traversal. In a small dictionary it may not matter much, but it is still unnecessary.

The preferred version is:

csharp
1if (dictionary.TryGetValue(key, out var value))
2{
3    Console.WriteLine(value);
4}

Now the lookup happens once.

When ContainsKey Is Fine

ContainsKey is still a good method when you genuinely only care about presence.

csharp
1if (!featureFlags.ContainsKey("new-dashboard"))
2{
3    Console.WriteLine("Flag is not configured");
4}

If no value is needed, TryGetValue can feel awkward because it forces you to name an out variable you may never use.

So the practical guideline is simple:

  • use ContainsKey for presence only
  • use TryGetValue when you want the value too

A Small Benchmark-Style Example

You do not need a benchmarking framework to understand the shape of the cost. This simple example shows the more efficient pattern for repeated reads:

csharp
1using System;
2using System.Collections.Generic;
3
4var scores = new Dictionary<string, int>
5{
6    ["alice"] = 10,
7    ["bob"] = 20,
8};
9
10var key = "alice";
11
12if (scores.TryGetValue(key, out var score))
13{
14    Console.WriteLine(score);
15}
16else
17{
18    Console.WriteLine("Missing");
19}

This avoids exceptions for missing keys and avoids the double lookup of ContainsKey plus indexer access.

Why Missing-Key Behavior Matters Too

Performance is not the only reason TryGetValue is preferred in many code paths. The indexer throws KeyNotFoundException when the key is absent, while TryGetValue gives you a branch-friendly boolean result.

That makes code both faster and clearer when missing keys are expected:

csharp
1if (!settings.TryGetValue("theme", out var theme))
2{
3    theme = "light";
4}

This is usually better than:

csharp
1string theme;
2
3try
4{
5    theme = settings["theme"];
6}
7catch (KeyNotFoundException)
8{
9    theme = "light";
10}

Exceptions are for exceptional situations, not normal control flow.

What About Worst Cases

Dictionary operations are fast on average, but hash collisions can make any hash-table operation slower. In ordinary application code with a sane comparer and normal key distribution, that is rarely the deciding factor between these two methods. The bigger concern is still avoiding unnecessary repeated lookups.

Common Pitfalls

  • Using ContainsKey and then the indexer when TryGetValue would do both jobs in one lookup.
  • Choosing the indexer for uncertain keys and then handling missing entries with exceptions.
  • Assuming ContainsKey is somehow safer than TryGetValue. The safer method is the one that matches what the code needs.
  • Over-optimizing microbenchmarks while ignoring clarity. The rule is simple: one lookup is better than two.
  • Forgetting that custom comparers affect dictionary behavior. Poor comparer choices can hurt all lookup methods.

Summary

  • 'ContainsKey and TryGetValue are both fast average-case dictionary operations.'
  • If you need the value, TryGetValue is usually better because it avoids a second lookup.
  • If you only need to know whether a key exists, ContainsKey is perfectly appropriate.
  • 'TryGetValue also avoids throwing exceptions for expected missing keys.'
  • The main performance lesson is not complexity class, but avoiding redundant work.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.