C# Dictionary: Complete Guide [2022]

A thumbnail showing C# code. Everything you need to know about C# Dictionary.

This article is a complete guide to the Dictionary data structure in C#.

C# Dictionary is a data structure that holds key-value pairs. It's called a Dictionary because the key is used to look up the corresponding value, just like in a real dictionary. The good thing is that the dictionary is a generic, so we can use it to store values of any type.

Dictionary usage

Initialization

To initialize a dictionary in C#, we first need to provide the type for the key and the type for the value.

For example, for a dictionary that stores the name and age of a person, the initialization would be:

csharp
Dictionary<string, int> nameToAge = new Dictionary<string, int>();

The first type inside the angle brackets is the key type. The second type is the value type.

Initial values

To define initial values at the time of initialization, we use the following syntax:

csharp
Dictionary<string, int> nameToAge = new Dictionary<string, int>()
{
	{"John", 26},
	{"Anna", 34 }
};

The collection-initializer syntax above is like the initialization of an array.

Another way to initialize a dictionary is object initializer introduced in C# 6:

csharp
Dictionary<string, int> nameToAge = new Dictionary<string, int>
{
	["John"] = 26,	
	["Anna"] = 34
};

Adding item

To add an item to a dictionary, we use the Add method. For example:

csharp
nameToAge.Add("Adam", 24);

This would add "Adam" and his age of 24 to the nameToAge dictionary.

Remove item

To remove an item from a dictionary, we use the Remove method. For example:

csharp
nameToAge.Remove("Adam");

This would remove "Adam" from the nameToAge dictionary.

The remove method uses the key to determine which item to remove.

Update items

To update an item in a dictionary, we update the value associated with the key. For example:

csharp
nameToAge["Adam"] = 24;

This would update Adam's age of 24 to nameToAge dictionary.

Remove all values

To remove all the values from a dictionary, we use the Clear method.

For example:

csharp
nameToAge.Clear();

This would clear nameToAge dictionary of all items.

Get the number of elements

To get the number of items in the dictionary, we use the Count property.

For example:

csharp
int count = nameToAge.Count;

This would get the number of items in nameToAge dictionary.

Duplicate values

In C#, a dictionary can only have one value for a key.

If we add another value with the same key, then we'll get the KeyNotFoundException exception.

For example, if we try to add a value with key "Adam" then we would get:

csharp
Dictionary<string, int> nameToAge = new ()
{
	{"Adam", 26},
};
nameToAge.Add("Adam", 24);

Here we get the exception because there is already a key "Adam" and the dictionary cannot have two values for the same key.

As developers, we need to handle these exceptions by first checking for the key to exist before adding a value.

To check if a dictionary already has a value, we can use the ContainsKey method:

csharp
nameToAge.ContainsKey("Adam")

It's a good design practice to use values we know are unique.

In the example above, it would be better to use a unique ID instead of a name.

TryGetValue

TryGetValue method is a safe way to get a value from a dictionary without having to handle exceptions.

It returns a bool value to let us know if the key was found.

For example, we can use TryGetValue this way:

csharp
int age = nameToAge.TryGetValue("Adam", out int a) ? a : default;

The code above will first check if the dictionary has a key "Adam" and if it does, returns its value. If it does not, then age will be set to the default value for int, which is 0.

The "Try" pattern is very common in C# and .NET, I explained it in detail in my article on TryParse.

Iterate over a Dictionary

To iterate over keys and values of a dictionary, we can use the foreach loop:

csharp
foreach (KeyValuePair<int, string> item in nameToAge)
{

  item.Key;
  item.Value;

}

The item variable is an object of type KeyValuePair<Tkey, TValue> which has the properties to provide the key and value.

KeyValuePair is a very simple struct. It only holds two properties - Key and Value:

KeyValuePair implementation.

Get keys and values

Dictionary exposes two read-only properties to get all keys or values:

These properties are useful if we want to iterate over all keys or values of a dictionary.

Keys and values source code.

Internals

C# dictionary uses a hash table as its internal data structure.

Hash tables rely on hash codes to locate an item.

For example, adding an item to a dictionary performs these 3 steps in the background:

  1. Compute a hash code for the key.
  2. Verify the key's hash code is not equal to the key already in the dictionary.
  3. Place the entry in the hash table bucket.

Read my article on Hashset to find out more about hash codes.

Dictionary and HashTable source code.

Use immutable objects for keys

Dictionary can break if we use a reference type as a key.

That's because Dictionary stores a reference to that object and not the object itself.

So if we change the object after adding it to the dictionary, then we would break the whole dictionary.

Because of that, it's a best practice to use immutable objects, like strings, when using the dictionary.

C# 9.0 in a Nutshell explains why:

If an object’s hashcode changes after it’s been added as a key to a dictionary, the object will no longer be accessible in the dictionary.

TryGetValue vs ContainsKey+Item

TryGetValue and ContainsKey are the two most popular ways of getting a value from a dictionary.

For ContainsKey, it's a two-step process:

  1. Do the lookup to verify the key exists in the dictionary.
  2. Do the lookup to get the value from the dictionary based on the key.

On the other hand, TryGetValue does the lookup and gets the value in one step.

Because of the performance, we should always use TryGetValue to get a value from a dictionary.

Dictionary exceptions

These are the most common exceptions we can get from a dictionary:

Exception typeException occurs
KeyNotFoundExceptionIf there is no key with the value we are looking up.
ArgumentOutOfRangeExceptionIf the index that we are looking up is out of range.
ArgumentNullExceptionIf the key is null.
ArgumentExceptionIf we try to add a key that already exists.

To prevent these exceptions, use TryGetValue and TryAdd.

Dictionary vs OrderedDictionary

The main difference between Dictionary and OrderedDictionary is that Dictionary does not guarantee the order in which it will return values, but OrderedDictionary does.

OrderedDictionary is less efficient than Dictionary because insert/delete operations have to be done on an array, with complexity of O(n) at worst.

Dictionary vs SortedDictionary

SortedDictionary is another class that implements IDictionary.

The main difference between a Dictionary and SortedDictionary is that SortedDictionary uses a binary search tree with O(log n) retrieval, while Dictionary is a hash table of O(1) complexity for access.

Use SortedDictionary if you have an extensive list of items and need them sorted.

ReadOnlyDictionary

ReadOnlyDictionary is an implementation of IDictionary that does not allow adding or deleting items.

It only provides read-access through its properties

ReadOnlyDictionary is a decorator wrapper around the regular Dictionary class.

Performance

The following table shows the complexity of each IDictionary implementation:

ImplementationComplexity
Dictionary, OrderedDictionaryO(1)
SortedDictionaryO(log n)
ListO(n)

Historically, computer scientists have been exploring the most efficient way of searching through a dictionary. Richard Bornat from Middlesex University explained the problem here:

Lecture from Richard Bornat about dictionaries.

Concurrency

The Dictionary implementation is not thread-safe.

For example, one thread can change the data structure while another thread tries to iterate, causing an exception.

To make a thread-safe dictionary in C#, use the ConcurrentDictionary implementation.

All public and protected members of ConcurrentDictionary are thread-safe and we can use them concurrently from multiple threads.

The ConcurrentDictionary implementation uses tables to keep track of dictionary's state:

ConcurrentDictionary class implementation in .NET

Summary

The Dictionary data structure in C# is a collection of key-value pairs.

It's called a Dictionary because the key is used to look up the corresponding value, just like in a real dictionary.

We can initialize the Dictionary using either a collection-initializer or an object initializer.

To manage a dictionary, we use:

  • Add method
  • Remove method
  • Clear method
  • Update the value associated with the key

Be careful and catch exceptions when using any of the methods that change the structure, such as Add, Remove, Clear, or Update.

The most common way to retrieve a value from a dictionary is using TryGetValue, which gets a value without throwing exceptions.

Besides the standard Dictionary, there are other implementation of IDictionary interface:

  • The Dictionary class does not guarantee the order in which it will return values, but OrderedDictionary does.
  • ReadOnlyDictionary does not allow adding or deleting items.
  • ConcurrentDictionary uses tables to keep track of dictionary's state from multiple threads, making it thread-safe.

FAQ

What are valid C# dictionary keys?

In C#, all keys that match the type of the dictionary are valid.

The only catch to be aware of is that the key cannot be null.

If we try to use null as a key, then we'll get an ArgumentNullException exception:

System.ArgumentNullException: Value cannot be null. (Parameter 'key')

Can we have a dictionary inside a dictionary?

Dictionary can have a dictionary as its value.

For example:

csharp
Dictionary<string, Dictionary<int, string>> nameToAgeByBirthDate = new Dictionary<string, Dictionary<int, string>>();

Here nameToAgeByBirthDate has 2 key-value pairs.

Even though C# supports nested dictionaries, I prefer to avoid them.

It makes the code more complex with no benefit. List or array is always a better choice.

Can key and value be of the same type?

In C#, key and value of a dictionary can have the same type. For example, to create a string-to-string mapping, we can use a Dictionary<string, string>.

Published on