Dictionary and ConcurrentDictionary in C#
Dictionary
A Dictionary
in C# is a generic collection that can be used to store key/value pairs, where each key is unique within the dictionary, and it maps to a specific value. The Dictionary class is implemented as a hash table, for this reason provides fast access to values based on keys. As an analogy, you can think of a Dictionary as a real-world dictionary, where you look up a word (key) to find its definition (value).
When you add a key-value pair to a Dictionary, it computes a hash-code from the key, which is used to determine the location where the key-value pair will be stored within the dictionary. Because of that, retrieving a value by using the Key in a Dictionary is very fast — close to O(1).
Dictionary<TKey, TValue>
class allows you to specify the data type for both the keys (TKey
) and the values (TValue
), for example, you can create a dictionary of integers as keys and strings as values (or any other data types), i.e: Dictionary<int, string>
. A Dictionary key must be unique and should not be null (for a reference type), while the value can be null (if it is a reference type).
A Dictionary is suitable when you need to have a collection and need to perform searching by using some key (Dictionary<TKey, TValue>
class). It can be used for tasks like custom data structures, configuration settings, where each setting is identified by a unique key, simple caching for temporarily storing data in memory for quick retrieval, data storage that needs to be quickly retrieved by a unique key, localization, where each key represents a term or phrase in one language and maps to the related translation in another language, error handling and logging, to store error codes or exception details and the corresponding error message).
Dictionary Examples
For demonstration purposes, I created a console application with .NET 7 and will present some coding examples explaining how to work with a Dictionary in C#. The project with the code can be found on my GitHub.
How to declare and initialize a Dictionary
For demonstration purposes, I’m going to use as an example a dictionary of payment methods, which receives an integer
as a Key
, and a string
as a Value
:
Dictionary<int, string> paymentMethods = new Dictionary<int, string>();
How to add Key/Value pairs to a Dictionary
In order to add key-value data to the Dictionary, you can use the method Add
:
paymentMethods.Add(1, "Credit Card");
paymentMethods.Add(2, "PayPal");
paymentMethods.Add(3, "Google Pay");
How to safely add Key/Values pair to a Dictionary
If you try to add another item using an existing key, an ArgumentException
error will be thrown. For instance, using the previous example where 3 items were added, if we try to add a new item using an existent key, the following error will be thrown:
paymentMethods.Add(3, "Cash");
The output will be an exception:
Unhandled exception. System.ArgumentException: An item with the same key has already been added. Key: 3
To avoid this, you can use the TryAdd
method. This method will add the item if the key does not exist in the Dictionary, and if exists it will do nothing and will not thrown an error. For example:
var cashAdded = paymentMethods.TryAdd(3, "Cash");
var criptoCointAdded = paymentMethods.TryAdd(4, "Bitcoin");
Console.WriteLine($"Was Cash added? {cashAdded}");
Console.WriteLine($"Was Bitcoin added? {criptoCointAdded}");
The output will be:
Was Cash added? False
Was Bitcoin added? True
How to iterate with Key and Values in a Dictionary
To check the items in a dictionary, you can use a foreach
loop instruction (or LINQ
) to access each of the items. For example:
foreach (var paymentMethod in paymentMethods)
{
Console.WriteLine($"Key: {paymentMethod.Key}, Value: {paymentMethod.Value}");
}
The output will be:
Key: 1, Value: Credit Card
Key: 2, Value: PayPal
Key: 3, Value: Google Pay
Key: 4, Value: Bitcoin
How to iterate over Keys and Values specifically
If you want to iterate specifically with only the Keys or only the Values, you can specify using the .Keys
or .Values
, for example:
var keys = paymentMethods.Keys;
foreach (var item in keys)
{
Console.WriteLine($"{item}");
}
And similar for Values:
var values = paymentMethods.Values;
foreach (var item in values)
{
Console.WriteLine($"{item}");
}
The output will be:
1
2
3
4
Credit Card
PayPal
Google Pay
Bitcoin
How to retrieve a target Value searching by Key
Now let’s say you need to return the target value of the item in the Dictionary for the Key number 2, for that you need to specify the dictionary and the Key between []
, for example:
var paymentMethod = paymentMethods[keyToSearch];
Console.WriteLine($"Payment Method with key {keyToSearch}: {paymentMethod}");
When executed, paymentMethod
will have the value PayPal
, as this is the item that contains the Key number 2 in this Dictionary. This will be the output:
Payment Method with key 2: PayPal
What happens when a Key does not exist?
When you search for a key that does not exist in the dictionary, a KeyNotFoundException
will be thrown. For example, the code below will result in the following error message:
var paymentMethod = paymentMethods[5];
The output of this operation will be:
System.Collections.Generic.KeyNotFoundException: The given key '4' was not present in the dictionary.
How to retrieve the Key searching by the target Value
In a Dictionary you can not directly retrieve the Key by searching by the target Value, however, you can perform a search like this by iterating through the Dictionary and comparing the values. In the example below, there is a filtering done using LINQ, which filters the Dictionary based on the value and then selects all the Keys that contain that specific value. The result of this search is added to a List<int>
variable:
var valueToSearch = "PayPal";
// Find keys by searching by the target value:
var keysWithValue = paymentMethods
.Where(kv => kv.Value == valueToSearch)
.Select(kv => kv.Key)
.ToList();
foreach (var key in keysWithValue)
{
Console.WriteLine($"Key with value {valueToSearch}: {key}");
}
The output will be:
Key with value PayPal: 2
Note that in this example, the result could be more than one item, as the Value
is not unique. In case you have multiple items which a specific value and want to return the first one, you can use FirstOrDefault()
instead of ToList()
.
How to safely get target Value searching by the Key
If you want to search for a value and want to avoid the KeyNotFoundException
error to be thrown, you can use the TryGetValue
method:
if (paymentMethods.TryGetValue(keyToSearch, out string resultValue))
{
Console.WriteLine($"Key {keyToSearch} was found. The target value is: {resultValue}");
}
else
{
Console.WriteLine($"The Key {keyToSearch} was not found.");
}
The output will be:
// For a non existent key:
The Key 5 was not found.
// For an existent key:
Key 4 was found. The target value is: Bitcoin
Searching with LINQ
It’s also possible to make use of LINQ
to perform searching in a dictionary. In case the key does not exist, it will not throw an exception.
In the example below you can see how to search by a specific key using LINQ.
var paymentMethod = paymentMethods
.Where(pair => pair.Key == keyToSearch)
.Select(pair => pair.Value)
.FirstOrDefault();
The output will be:
// For an existent key:
Payment method 2: PayPal
// For a non existent key:
Payment method 6 was not found
You can also do something similar for searching by the target value, in this case, more than one result might be returned (because different from the key, the target value is not unique). For example:
var paymentMethod = paymentMethods
.Where(pair => pair.Key == keyToSearch)
.Select(pair => pair.Value)
.FirstOrDefault();
if (paymentMethod != null)
{
Console.WriteLine($"Payment method {keyToSearch}: {paymentMethod}");
}
else
{
Console.WriteLine($"Payment method {keyToSearch} was not found");
}
The output will be:
// When more than one key-value pair with target value "PayPal" exists:
Payment method(s) named 'PayPal' found with key(s): 2, 5
// When target value was not found in the dictionary:
Payment method 'Cash' was not found
Check if a Key or Value exists with Contains
If you need to check if a Key or a Value exists in a dictionary, you can use the ContainsKey
and the ContainsValue
methods, which return a boolean:
var containsKey1 = paymentMethods.ContainsKey(1);
var containsKey5 = paymentMethods.ContainsKey(5);
Console.WriteLine($"Contains Key 1: {containsKey1}");
Console.WriteLine($"Contains Key 5: {containsKey5}");
var containsValuePayPal = paymentMethods.ContainsValue("PayPal");
var containsValueCash = paymentMethods.ContainsValue("Cash");
Console.WriteLine($"Contains Value PayPal: {containsValuePayPal}");
Console.WriteLine($"Contains Value Cash: {containsValueCash}");
The output will be:
Contains Key 1: True
Contains Key 5: False
Contains Value PayPal: True
Contains Value Cash: False
How to update the Value of a Key in a Dictionary?
Suppose now we need to update the value of the Key number 3 in the dictionary, you can easily update it by passing the Key and the new Value:
var key = 3;
Console.WriteLine($"Value of Key 3 before the update: {paymentMethods[key]}");
// Update the value of the item with Key number 3:
paymentMethods[key] = "Apple Pay";
Console.WriteLine($"Value of Key {key} after the update: {paymentMethods[key]}");
The output will be:
Value of Key 3 before the update: Google Pay
Value of Key 3 after the update: Apple Pay
How to update a Key in a Dictionary?
A Key in a Dictionary is immutable (because they are used to create a hash, which ensures efficient lookup and retrieval), this means that we can not update an existent Key. However, you can achieve a similar effect of an “update” by adding a new key-value pair and deleting the previous one:
var keyToBeRecriated = 3;
var newKeyToBeCreated = 5;
if (paymentMethods.ContainsKey(keyToBeRecriated))
{
// Step 1: Add a new key-value pair with the updated key:
paymentMethods[newKeyToBeCreated] = paymentMethods[keyToBeRecriated];
// Step 2: Remove the old key:
paymentMethods.Remove(keyToBeRecriated);
}
// Display the updated dictionary
foreach (var paymentMethod in paymentMethods)
{
Console.WriteLine($"{paymentMethod.Key}: {paymentMethod.Value}");
}
In this example, the item with Key number 3 (“Apple Pay”) was removed, and a new item was added to the dictionary, with Key number 4 and Value “Apple Pay”. The output will be:
Key: 1, Value: Credit Card
Key: 2, Value: PayPal
Key: 4, Value: Apple Pay
How to check how many items exist in a Dictionary
If you need to check the amount of items in a dictionary, you can use the Count
method from LINQ
:
var dictionarySize = paymentMethods.Count();
Console.WriteLine($"Dictionary size: {dictionarySize}");
The output will be:
Dictionary size: 4
How to remove an item in a Dictionary
To remove an item in a Dictionary we can use the method Remove
. This method will remove a key-value pair in case the Key exists in the dictionary, and in case the key does not exist, it will do nothing.
// Remove an item based on the Key:
paymentMethods.Remove(keyToBeRemoved);
// or
// Which returns a boolean value (true when item is removed, and false when is not removed):
var wasRemoved = paymentMethods.Remove(keyToBeRemoved);
ConcurrentDictionary
A ConcurrentDictionary
is a thread-safe collection of Key/Value pairs that can be accessed by multiple threads concurrently, and ensures data integrity. This class is designed to handle concurrent access from multiple threads avoiding race conditions — which is a problem that can occur when two or more threads access and modify shared data or resources at the same time, leading to unpredictable behaviour such as data corruption, incorrect results, exceptions, etc. When you need a thread-safe collection of Key/Value pairs, that can be accessed by multiple threads concurrently then you can use the ConcurrentDictionary<TKey,TValue>
class.
“Members accessed through one of the interfaces the ConcurrentDictionary<TKey,TValue> implements, including extension methods, are not guaranteed to be thread safe and may need to be synchronized by the caller.” (Microsoft Docs)
You can initialize a ConcurrentDictionary like this:
ConcurrentDictionary<int, string> paymentMethodsConcurrentDictionary = new ConcurrentDictionary<int, string>();
How to add an item in a ConcurrentDictionary
Similar to adding an item into a Dictionary, to add an item into a ConcurrentDictionary you can make use of the TryAdd
method.
How to search for an item in a ConcurrentDictionary
To get a key-value pair in a ConcurrentDictionary, you can make use of the TryGetMethod
. This method will search for an item based on a Key, and will return a boolean (true when the item was found and false when was not found), without throwing an exception in case the key does not exist. For example:
var existentKey = 2;
if (paymentMethodsConcurrentDictionary.TryGetValue(existentKey, out string valueForExistingKey))
{
Console.WriteLine($"Key {existentKey} was found. Value: {valueForExistingKey}");
}
else
{
Console.WriteLine($"Key {existentKey} was not found");
}
var nonExistentKey = 6;
if (paymentMethodsConcurrentDictionary.TryGetValue(nonExistentKey, out string valueForNonExistingKey))
{
Console.WriteLine($"Updated Value for {nonExistentKey}: {valueForNonExistingKey}");
}
else
{
Console.WriteLine($"Key {nonExistentKey} was not found\n");
}
The output will be:
The Key 2 was removed. Value: PayPal
The Key 6 was not found
If you need to search for a target value, you can follow the same approach with the Dictionary example using LINQ
.
How to Add Or Update items in a ConcurrentDictionary
To add a new key-value pair or to update the target value of an existent item, the method TryAdd
or AddOrUpdate
can be used. The method TryAdd
works in a similar way as presented before for a normal dictionary, so I’m going to focus on this topic on the AddOrUpdate
method. This method receives three parameters:
- 1st: The key to be added/updated
- 2nd parameter: The Value to be added in case the key does not exist in the dictionary
- 3rd: A function that returns the value to be updated in the dictionary in case the key already exists. This function takes two parameters, the key and a value.
This is an example of how to use the AddOrUpdate
method. In the first block of code, an existent value will be updated, and in the second block, a new key-value pair will be added:
// In this example, the key-value pair with Key 3 will be updated:
var keyToBeUpdated = 3;
paymentMethodsConcurrentDictionary.AddOrUpdate(
keyToBeUpdated,
"Apple Pay (Added)", // Value to add if the the key does not exist;
(key, oldValue) => "Apple Pay (Updated)" // Update the existing value;
);
// In this example, a new key-value pair with Key 4 will be added:
var keyToBeAdded = 4;
paymentMethodsConcurrentDictionary.AddOrUpdate(
keyToBeAdded,
"Cash (Added)", // Value to add since the key does not exist;
(key, oldValue) => oldValue // No update is performed in this case;
);
// Display the updated dictionary:
foreach (var keyValuePair in paymentMethodsConcurrentDictionary)
{
Console.WriteLine($"Key: {keyValuePair.Key}, Value: {keyValuePair.Value}");
}
The output will be:
Key: 1, Value: Credit Card
Key: 2, Value: PayPal
Key: 3, Value: Apple Pay (Updated)
Key: 4, Value: Cash (Added)
How to Remove key-value pairs in a ConcurrentDictionary
In order to Remove an item in a ConcurrentDictionary, you can use TryRemove
:
var keyRemoved = paymentMethodsConcurrentDictionary.TryRemove(keyToBeRemoved, out string removedValue);
if (keyRemoved)
{
Console.WriteLine($"The Key {keyToBeRemoved} was removed. Value: {removedValue}");
}
else
{
Console.WriteLine($"The Key {keyToBeRemoved} was not found.");
}
The output will be:
// For an existent key:
The Key 2 was removed. Value: PayPal
// For a non existent key:
The Key 6 was not found.
Conclusion
A Dictionary
is an efficient data structure in C# that provides a flexible and powerful way to manage key-value pairs with high efficiency. The Dictionary class is implemented as a hash table, allowing you to have fast access to values based on keys. You can make use of Dictionary
for single-thread scenarios and ConcurrentDictionary
for multi-thread scenarios, avoiding race conditions. This article presented how to create and work with a Dictionary, performing a series of operations using this data structure.
This is the link for the project in GitHub: https://github.com/henriquesd/DictionaryDemo
If you like this demo, I kindly ask you to give a ⭐️ in the repository.
Thanks for reading!