The C# dictionary is one of the most important collection types/data structures you’ll use while developing your applications. You can use a dictionary to solve certain kinds of problems in a way that’s much more natural and elegant than using, say, a list. There are also significant performance gains you can obtain by using dictionaries.
That’s what this post is about: a detailed introduction to this powerful collection type. We’ll cover:
Let’s start by covering some C# dictionary fundamentals.
In C#, the dictionary class allows you to create a collection of key-value pairs. A key is a unique identifier through which you can later retrieve the value. And the value is whatever you need to store and later retrieve.
What is a dictionary good for? There are certain performance-sensitive scenarios in which dictionaries shine, and we’ll cover more of that later. But in general, dictionaries are well-suited for when you need to retrieve a unique value based on its identifier, in an efficient way.
Using a dictionary in C# allows you to:
Time to roll up your sleeves and learn how to work with a C# dictionary.
The easiest way to declare a dictionary is using the class’s constructor to initialize an empty one:
var dic = new Dictionary<int, string>();
In the example above, we create a dictionary whose keys are of type int and values are of type string.
Let’s say that you would like to use the dictionary from the previous example to store the names of the months, in relation to their numbers, in a one-based fashion. Use the Add method, passing first the key and then the value as arguments:
dic.Add(1, "January");
dic.Add(2, "February");
dic.Add(3, "March");
dic.Add(4, "April");
dic.Add(5, "May");
dic.Add(6, "June");
dic.Add(7, "July");
dic.Add(8, "August");
dic.Add(9, "September");
dic.Add(10, "October");
dic.Add(11, "November");
dic.Add(12, "December");
Alternatively, it is also possible to create the dictionary and add the items in one go:
var dic = new Dictionary<int, string>
{
{ 1, "January" },
{ 2, "February" },
{ 3, "March" },
{ 4, "April" },
{ 5, "May" },
{ 6, "June" },
{ 7, "July" },
{ 8, "August" },
{ 9, "September" },
{ 10, "October" },
{ 11, "November" },
{ 12, "December" }
};
The astute readers will remember that, when defining the dictionary, the key must be unique. So, what happens when you try to add an element with an already existing key? You get an exception, of course. Let’s say that after initializing the dictionary with all twelve months, you try to add another value with the key 12.
You would get a System.ArgumentException with the following message: ‘An item with the same key has already been added. Key: 12’.
These aren’t all the ways you can initialize a dictionary, but the examples are certainly the most common ones.
Let’s cover the most common methods from the Dictionary class.
You access dictionary elements using its indexer and providing a key. In the following example, we retrieve the names of all months by using numbers from 1 to 12 as the keys:
var monthNumbers = Enumerable.Range(1, 12);
foreach (int month in monthNumbers)
{
Console.WriteLine($"The #{month} of the year is: {dic[month]}");
}
What happens if you pass the dictionary a key it doesn’t contain? You get an exception. More specifically, a System.Collections.Generic.KeyNotFoundException. And that ties nicely with the next topic.
If, within your code, you can’t know for sure whether a key exists, then you should verify its presence before you try to access it. The same applies when adding elements: if you don’t know whether a key has already been added to the dictionary, check first. You can do that by using the ContainsKey() method:
if (dic.ContainsKey(12))
{
var lastMonthName = dic[12];
}
Unlike keys, it’s completely valid for a dictionary to have duplicated values. Duplicate values may or may not make sense for your specific use case, and that’s why you might have to use the ContainsValue() method to check for a value’s presence.
Alternatively, a more performant way to test for the existence of a value and getting it at the same time is using the TryGetValue() method.
The regular Dictionary class in C# is mutable, which means you can change the values you added to it. See the following example:
var helloWorldTranslations = new Dictionary<string, string>
{
{ "en", "Hello, world!" },
{ "es", "Hola, mundo!" },
{ "pt", "Olá, mundo" }
};
// oops, we forgot the upside-down exclamation mark for the Spanish one!
helloWorldTranslations["es"] = "¡Hola, mundo!";
Console.WriteLine(helloWorldTranslations["es"]); // prints '¡Hola, mundo!'
Use the Remove() method to remove an element from a dictionary. This method has a few overloads, but the most well-known one receives a single argument, representing the key for the pair you wish to remove.
This method returns true if the specified key was found and the associate pair was removed, and false otherwise. That means this method, unlike others we’ve seen before, doesn’t throw an error when you provide a key that can’t be found.
if (helloWorldTranslations.Remove("es"))
{
Console.WriteLine("Spanish translation removed");
}
What if you wanted to remove all items from the dictionary in one go? In that case, the Clear() method is what you’re looking for:
helloWorldTranslations.Clear();
There are several ways to iterate over a dictionary using a foreach loop. For starters, here’s our sample dictionary for this example:
Dictionary<string, string> programmingAuthors = new();
programmingAuthors["Robert C. Martin"] = "Clean Code";
programmingAuthors["Donald Knuth"] = "The Art of Computer Programming";
programmingAuthors["Gang of Four"] = "Design Patterns";
programmingAuthors["Martin Fowler"] = "Refactoring";
programmingAuthors["Thomas H. Cormen"] = "Introduction to Algorithms";
programmingAuthors["Andrew S. Tanenbaum"] = "Modern Operating Systems";
Notice two things of interest here:
With that in place, let’s start by first iterating through the key-value pairs (the code below uses explicit typing for the foreach variable, but of course, we can type inference as well):
foreach (KeyValuePair<string, string> author in programmingAuthors)
{
Console.WriteLine($"Author: {author.Key}, Famous Book: {author.Value}");
}
Let’s do it again, but this time using the deconstruction syntax introduced in C# 7:
foreach (var (author, book) in programmingAuthors)
{
Console.WriteLine($"Author: {author}, Famous Book: {book}");
}
Finally, let’s see how to iterate over only the keys and then only the values:
// If you only need the authors
foreach (string author in programmingAuthors.Keys)
{
Console.WriteLine($"Author: {author}");
}
// If you only need the books
foreach (string book in programmingAuthors.Values)
{
Console.WriteLine($"Book: {book}");
}
Generally speaking, dictionaries are fast. Here’s what .NET’s documentation has to say about element retrieval:
“Retrieving a value by using its key is very fast, close to O(1), because the Dictionary<TKey,TValue> class is implemented as a hash table.”
If you don’t know what “O(1)” means, you should learn about “time complexity” and “Big-O notation.” In short, the sentence above means that retrieving a value by its key takes a fixed time, regardless of the number of elements in the dictionary.
That isn’t true 100% of the time, though:
“The speed of retrieval depends on the quality of the hashing algorithm of the type specified for TKey.”
The type used as key should have a sound algorithm for its GetHashCode() method. If different objects return the same code – in other words if there are collisions – the performance of lookups will be impacted.
Dictionaries excel in scenarios in which you need fast retrieval of an element based on its associated key. Considering this, here’s a non-exhaustive list of common use cases for dictionaries:
Before wrapping up, let’s cover some dictionary best practices you should be aware of:
In this post, you’ve learned what a dictionary is and how and when to use it. As you’ve seen, dictionaries are great when you need a fast lookup of values. That doesn’t mean, however, that dictionaries are entirely free of potential pitfalls when it comes to performance. For instance, dictionaries have a bigger memory footprint than, say, lists, because of how they’re implemented internally (dictionaries potentially need to have several arrays and linked lists).
So, this bigger overhead is something you should be aware of and, in scenarios where the fast lookup of dictionaries doesn’t make a difference, consider picking another type.
Also, regardless of whether you’re using dictionaries, it’s great to have the support of an application monitoring tool for your applications that alerts you at the first sign of potential performance problems. In that spirit, we invite you to start your free trial of Stackify today.
Stackify's APM tools are used by thousands of .NET, Java, PHP, Node.js, Python, & Ruby developers all over the world.
Explore Retrace's product features to learn more.