Home
Search
C# Dictionary ExamplesPerform fast lookups with string keys. Add elements to Dictionary from System.Collections.Generic.
dot net perls
Dictionary. Programs often map keys to values. Consider a program that maps animal names to their IDs: "cat" maps to 2. A Dictionary can store this relation.
Dictionary keys. The Dictionary is a fast way to remember things. A key (like a string) maps to a value (like an int). Add() inserts keys and values.
ToDictionary
First example. We add 4 keys (each with an int value) to 2 separate Dictionary instances. Every Dictionary has pairs of keys and values.
String, int Dictionary is used with different elements. We specify its key type and its value type (string, int).
Version 1 We use Add() to set 4 keys to 4 values in a Dictionary. Then we access the Count property to see how many items we added.
Version 2 This creates a Dictionary with the same internal values as the first version, but its syntax is more graceful.
C# program that uses Dictionary
using System; using System.Collections.Generic; class Program { static void Main() { // Version 1: create a Dictionary, then add 4 pairs to it. var dictionary = new Dictionary<string, int>(); dictionary.Add("cat", 2); dictionary.Add("dog", 1); dictionary.Add("llama", 0); dictionary.Add("iguana", -1); // The dictionary has 4 pairs. Console.WriteLine("DICTIONARY 1: " + dictionary.Count); // Version 2: create a Dictionary with an initializer. var dictionary2 = new Dictionary<string, int>() { {"cat", 2}, {"dog", 1}, {"llama", 0}, {"iguana", -1} }; // This dictionary has 4 pairs too. Console.WriteLine("DICTIONARY 2: " + dictionary2.Count); } }
DICTIONARY 1: 4 DICTIONARY 2: 4
TryGetValue. This is often the most efficient lookup method. In my experience, most of the time we want to use TryGetValue—so learn how to use it right away when learning Dictionary.
TryGetValue
Part A We invoke TryGetValue, which tests for the key. It then returns the value if it finds the key.
Part B Do another look up with TryGetValue. Declare the local variable inside the if-condition.
C# program that uses TryGetValue
using System; using System.Collections.Generic; class Program { static void Main() { var values = new Dictionary<string, string>(); values.Add("A", "uppercase letter A"); values.Add("c", "lowercase letter C"); // Part A: local variable result. string result; if (values.TryGetValue("c", out result)) { Console.WriteLine(result); } // Part B: use inline "out string." if (values.TryGetValue("c", out string description)) { Console.WriteLine(description); } } }
lowercase letter C lowercase letter C
Foreach. Let us loop over the data in a Dictionary. With each KeyValuePair, there are Key and Value properties—this gives us all data stored.
Foreach
Part 1 The code creates a Dictionary with string keys and int values. The Dictionary stores animal counts.
Part 2 In this foreach-loop, each KeyValuePair struct has 2 members, a string Key and an int Value.
KeyValuePair
Part 3 Instead of specifying KeyValuePair directly, often we can just use var for simpler code.
Var
C# program that uses foreach on Dictionary
using System; using System.Collections.Generic; class Program { static void Main() { // Part 1: initialize data. Dictionary<string, int> data = new Dictionary<string, int>() { {"cat", 2}, {"dog", 1}, {"llama", 0}, {"iguana", -1} }; // Part 2: loop over pairs with foreach. foreach (KeyValuePair<string, int> pair in data) { Console.WriteLine("FOREACH KEYVALUEPAIR: {0}, {1}", pair.Key, pair.Value); } // Part 3: use var keyword to enumerate Dictionary. foreach (var pair in data) { Console.WriteLine("FOREACH VAR: {0}, {1}", pair.Key, pair.Value); } } }
FOREACH KEYVALUEPAIR: cat, 2 FOREACH KEYVALUEPAIR: dog, 1 FOREACH KEYVALUEPAIR: llama, 0 FOREACH KEYVALUEPAIR: iguana, -1 FOREACH VAR: cat, 2 FOREACH VAR: dog, 1 FOREACH VAR: llama, 0 FOREACH VAR: iguana, -1
Keys. The Keys property returns a collection of type KeyCollection, not an actual List. We can convert it into a List. This property is helpful in many programs.
C# program that gets Keys
using System; using System.Collections.Generic; class Program { static void Main() { var data = new Dictionary<string, int>() { {"cat", 2}, {"dog", 1}, {"llama", 0}, {"iguana", -1} }; // Store keys in a List. var list = new List<string>(data.Keys); // Loop through the List. foreach (string key in list) { Console.WriteLine("KEY FROM LIST: " + key); } } }
KEY FROM LIST: cat KEY FROM LIST: dog KEY FROM LIST: llama KEY FROM LIST: iguana
Count and Remove. Count returns the number of keys in a dictionary. And Remove eliminates a key (and its value) from the dictionary—if the key is found.
Count This property returns the total number of keys. Count is the simplest way to count all pairs in a Dictionary.
Count
Warning Remove will not cause an error if the key is not found, but if the key is null, it will cause an exception.
C# program that counts keys, and removes key
using System; using System.Collections.Generic; class Program { static void Main() { var lunch = new Dictionary<string, int>() { {"carrot", 1}, {"pear", 4}, {"apple", 6}, {"kiwi", 3} }; Console.WriteLine("COUNT: " + lunch.Count); // Remove pear. lunch.Remove("pear"); Console.WriteLine("COUNT: " + lunch.Count); } }
COUNT: 4 COUNT: 3
GetValueOrDefault. This method is available on Dictionary in .NET 5. It safely (with no possible exceptions) gets a value from the Dictionary, or the default value for the value's type.
Info Value types have a default of 0 (or the equivalent to 0) or null. The default can be determined with a default() call.
Here The dictionary is created with a key of "mittens," and we get its value with GetValueOrDefault.
And When we try to get a key that does not exist, the value 0 is returned—0 is the default value for an int.
C# program that calls GetValueOrDefault
using System; using System.Collections.Generic; class Program { static void Main() { var cats = new Dictionary<string, int>() { { "mittens", 5 } }; // Has value 5. Console.WriteLine(cats.GetValueOrDefault("mittens")); // Not found. Console.WriteLine(cats.GetValueOrDefault("?")); } }
5 0
TryAdd. With this method (part of .NET 5) we add an entry only if the key is not found. It returns a bool that tells us whether the key was added.
Part 1 We call TryAdd, and the key is not found in the dictionary, so it is added with the value we specify (1).
Part 2 We call TryAdd again. But "test" already is present, so TryAdd returns false and the dictionary is not modified.
Part 3 We use GetValueOrDefault and the initial value of "test" is still in the dictionary.
C# program that uses Dictionary TryAdd method
using System; using System.Collections.Generic; class Program { static void Main() { var items = new Dictionary<string, int>(); // Part 1: add the string with value 1. bool result = items.TryAdd("test", 1); Console.WriteLine("Added: " + result); // Part 2: the value already exists, so we cannot add it again. bool result2 = items.TryAdd("test", 2); Console.WriteLine("Added: " + result2); // Part 3: the value is still 1. Console.WriteLine(items.GetValueOrDefault("test")); } }
Added: True Added: False 1
ContainsKey. This commonly-called method sees if a given string is present in a Dictionary. We use string keys here—we look at more types of Dictionaries further on.
ContainsKey
Returns ContainsKey returns true if the key is found. Otherwise, it returns false. No exception is thrown if the key is missing.
C# program that uses ContainsKey
using System; using System.Collections.Generic; class Program { static void Main() { Dictionary<string, int> dictionary = new Dictionary<string, int>(); dictionary.Add("apple", 1); dictionary.Add("windows", 5); // See whether Dictionary contains this string. if (dictionary.ContainsKey("apple")) { int value = dictionary["apple"]; Console.WriteLine(value); } // See whether it contains this string. if (!dictionary.ContainsKey("acorn")) { Console.WriteLine(false); } } }
1 False
Int keys. Dictionary is a generic class. To use it, we must specify a type. This is a good feature—it means we can use an int key just as easily as a string key.
Int In this program, we see an example of a Dictionary with int keys. The values can also be any type.
C# program that uses int keys
using System; using System.Collections.Generic; class Program { static void Main() { // Use int key type. var data = new Dictionary<int, string>(); data.Add(100, "color"); data.Add(200, "fabric"); // Look up the int. if (data.ContainsKey(200)) { Console.WriteLine("CONTAINS KEY 200"); } } }
CONTAINS KEY 200
LINQ. Extension methods can be used with Dictionary. We use the ToDictionary method—this is an extension method on IEnumerable. It places keys and values into a new Dictionary.
Lambda The program uses lambda expressions. With these small functions, we specify a method directly as an argument.
Lambda
Here The example uses ToDictionary, from System.Linq, on a string array. It creates a lookup table for the strings.
C# program that uses System.Linq, ToDictionary
using System; using System.Linq; class Program { static void Main() { string[] values = new string[] { "One", "Two" }; // Create Dictionary with ToDictionary. // ... Specify a key creation method, and a value creation method. var result = values.ToDictionary(item => item, item => true); foreach (var pair in result) { Console.WriteLine("RESULT PAIR: {0}, {1}", pair.Key, pair.Value); } } }
RESULT PAIR: One, True RESULT PAIR: Two, True
ContainsValue. This method tries to find a value in the Dictionary (not a key). It searches the entire collection, as no hashing is available on values.
ContainsValue
Note This example will loop through all elements in the Dictionary until it finds a match, or there are no more elements to check.
Speed ContainsValue is not as fast as ContainsKey. Using another Dictionary with keys of the values could help speed things up.
C# program that uses ContainsValue
using System; using System.Collections.Generic; class Program { static void Main() { var data = new Dictionary<string, int>(); data.Add("cat", 1); data.Add("dog", 2); // Use ContainsValue to see if the value is present with any key. if (data.ContainsValue(1)) { Console.WriteLine("VALUE 1 IS PRESENT"); } } }
VALUE 1 IS PRESENT
Indexer. We do not need to use Add to insert into a Dictionary. We can instead use the indexer with square brackets. This syntax also gets the value at the key.
Caution If we try to get a value at a key that doesn't exist, an exception is thrown.
Note With the indexer, an exception is not thrown when we assign to a key that already has a value. But with Add, an exception is thrown.
C# program that uses Dictionary indexer
using System; using System.Collections.Generic; class Program { static void Main() { Dictionary<int, int> dictionary = new Dictionary<int, int>(); // We can assign with the indexer. dictionary[1] = 2; dictionary[2] = 1; dictionary[1] = 3; // Reassign. // Read with the indexer. // ... An exception occurs if no element exists. Console.WriteLine(dictionary[1]); Console.WriteLine(dictionary[2]); } }
3 1
Copy. Dictionary provides a constructor that copies all values and keys into a new Dictionary instance. This constructor improves code reuse. It makes copying simpler.
Copy Dictionary
Test The "test" Dictionary in this program is created, and has just 1 key and value pair in it.
Copy We create a copy by passing the "test" Dictionary to its constructor. We add a key. The copy has 2 keys—but "test" still has 1.
C# program that copies Dictionary
using System; using System.Collections.Generic; class Program { static void Main() { var test = new Dictionary<int, int>(); test[20] = 30; // Copy the dictionary, and add another key, so we now have 2 keys. var copy = new Dictionary<int, int>(test); copy[30] = 40; Console.WriteLine("TEST COUNT: {0}", test.Count); Console.WriteLine("COPY COUNT: {0}", copy.Count); } }
TEST COUNT: 1 COPY COUNT: 2
Dictionary field. Sometimes it is useful to have a Dictionary at the class level, as a field. And if we have a static class, we should initialize the Dictionary at the class level.
Note Avoid the static constructor—static constructors often carry performance penalties.
C# program that uses Dictionary field
using System; using System.Collections.Generic; class Example { Dictionary<int, int> _lookup = new Dictionary<int, int>() { {1, 1}, {2, 3}, {3, 5}, {6, 10} }; public int GetValue() { return _lookup[2]; // Example only. } } class Program { static void Main() { // Create Example object instance. var example = new Example(); // Look up a value from the Dictionary field. Console.WriteLine("RESULT: " + example.GetValue()); } }
RESULT: 3
KeyNotFoundException. This error happens when we access a nonexistent key. With Dictionary we should test keys for existence first, with ContainsKey or TryGetValue.
KeyNotFoundException
C# program that shows KeyNotFoundException
using System.Collections.Generic; class Program { static void Main() { var colors = new Dictionary<string, int>() { { "blue", 10 } }; // Use ContainsKey or TryGetValue instead. int result = colors["fuschia"]; } }
Unhandled Exception: System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary. at System.Collections.Generic.Dictionary`2.get_Item(TKey key) at Program.Main() in ...
Benchmark, StringComparer. When you do a lookup on a Dictionary of string keys, the string must be hashed. If we pass StringComparer.Ordinal to our Dictionary, we have a faster algorithm.
Version 1 We look up the key "Las Vegas" in a Dictionary with no comparer specified in its constructor.
Version 2 In this version we look up the string "Las Vegas" in a Dictionary with StringComparer.Ordinal.
Result When we consider a string as "ordinal," we but gain performance on comparison and hashing (tested in 2021 on .NET 5).
C# program that times StringComparer.Ordinal
using System; using System.Collections.Generic; using System.Diagnostics; class Program { const int _max = 10000000; static void Main() { var data1 = new Dictionary<string, bool>(); data1["Las Vegas"] = true; var data2 = new Dictionary<string, bool>(StringComparer.Ordinal); data2["Las Vegas"] = true; // Version 1: do lookups on Dictionary with default string comparisons. Stopwatch s1 = Stopwatch.StartNew(); for (int i = 0; i < _max; i++) { if (!data1.ContainsKey("Las Vegas")) { return; } } s1.Stop(); // Version 2: do lookups in Dictionary that has StringComparer.Ordinal. Stopwatch s2 = Stopwatch.StartNew(); for (int i = 0; i < _max; i++) { if (!data2.ContainsKey("Las Vegas")) { return; } } s2.Stop(); Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); } }
16.40 ns ContainsKey, default comparer 15.17 ns ContainsKey, StringComparer.Ordinal
Benchmark, Keys. Here we compare loops. A foreach-loop on KeyValuePairs is faster than the looping over Keys and accessing values in the loop body.
Version 1 This code loops over the dictionary in a foreach-loop directly, without accessing the Keys property.
Version 2 This version of the code accesses the Keys property, and then looks up each value in a foreach-loop.
Result In 2021 with .NET 5 (for Linux) it is faster to loop over the pairs in a Dictionary and avoid lookups.
C# program that benchmarks foreach on Dictionary
using System; using System.Collections.Generic; using System.Diagnostics; class Program { static void Main() { var test = new Dictionary<string, int>(); test["bird"] = 10; test["frog"] = 20; test["cat"] = 60; int sum = 0; const int _max = 1000000; // Version 1: use foreach loop directly on Dictionary. var s1 = Stopwatch.StartNew(); for (int i = 0; i < _max; i++) { foreach (var pair in test) { sum += pair.Value; } } s1.Stop(); // Version 2: use foreach loop on Keys, then access values. var s2 = Stopwatch.StartNew(); for (int i = 0; i < _max; i++) { foreach (var key in test.Keys) { sum += test[key]; } } s2.Stop(); Console.WriteLine(s1.Elapsed.TotalMilliseconds); Console.WriteLine(s2.Elapsed.TotalMilliseconds); } }
22.5618 ms Dictionary foreach 72.231 ms Keys foreach
Benchmark, key length. When a string key is looked up in a Dictionary, it must be hashed and compared. Logically a shorter string key should be faster, as less data is being tested.
Version 1 This code looks up a short string key in the Dictionary—each lookup is just 4 chars.
Version 2 Here we look up a string key that is longer. Each lookup succeeds (in the same way as version 1).
Result In benchmarking the program on .NET 5 on Linux, the shorter string key is faster.
C# program that times string key length
using System; using System.Collections.Generic; using System.Diagnostics; class Program { const int _max = 10000000; static void Main() { var places = new Dictionary<string, int>() { { "Alps", 1 }, { "Appalachian", 2 } }; if (!places.ContainsKey("Alps")) { return; } // Version 1: look up a short string key. var s1 = Stopwatch.StartNew(); for (int i = 0; i < _max; i++) { if (!places.ContainsKey("Alps")) { return; } } s1.Stop(); // Version 2: look up a long string key. var s2 = Stopwatch.StartNew(); for (int i = 0; i < _max; i++) { if (!places.ContainsKey("Appalachian")) { return; } } s2.Stop(); Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); } }
15.86 ns 4-char string key 16.41 ns 12-char string key
Clear. We can erase all pairs with the Clear method. Or we can assign the Dictionary variable to null. This causes little difference in memory usage—the entries are garbage-collected.
Dictionary Clear
Return. A Dictionary can be returned, or passed as an argument. The Dictionary type is defined as a class. It is always passed as a reference type.
And This means only 32-64 bits will be copied on the method invocation. The same principle applies when returning values.
Return
Custom methods. Knowing how to use the methods on a Dictionary is only the first accomplishment. As a developer you will need to design custom methods that use dictionaries.
Dictionary, Binary File
Dictionary, Combine Keys
Dictionary, Equals
Dictionary, Sort
IEqualityComparer. Dictionary uses an IEqualityComparer to compute the hash code for its keys. We can implement this interface with a class. This can improve performance.
IEqualityComparer
Map. The Dictionary is a map. A real map helps us find our destination, but a digital map directs us from a key to a value. In languages we often find a map type.
Map
Case-insensitive keys. With a special StringComparer we can have case-insensitive key lookups on a dictionary. This reduces the need to call ToLower. It reduces string creation.
Case, Dictionary
A summary. The C# Dictionary, like all hash tables, is fascinating. It is fast—and useful in many places. Once we get a handle on its syntax, it is easy to use.
Home
© 2007-2021 sam allen. send bug reports to info@dotnetperls.com.