Start learning free

LESSON 235 OF 347 · HASH TABLE

Hash Maps in C#: Using Dictionary<TKey, TValue>

Lesson Read the full explanation

Hash Tables, better known as Hash Maps, are just collections of key-value pairs. In other words, they are pieces of data (values) mapped to unique identifiers called properties (keys).

Hash Maps were designed to give us a way of, given a key, associating a value with it for very quick lookups.

In Java, the Hash Map is implemented as a Dictionary


Creation:

You create an empty Hash Map by declaring an Map<keyType, valueType> and initializing it with using new Dictionary<>(), where the keyType and valueType are the data types of the keys and values.

Let's declare a Hash Map that maps text values represing digits to their numerical value (e.g. "two" => 2). We wil map strings to integers, so the two types are string and int. We will name the map digitValue

Map<string, int> digitValue = new Dictionary<>();

Creating an empty dictionary takes O(1) time.


Initialization:

You can also add some key-value pairs in your dictionary at creation:

Map<string, int> digitValue = new Dictionary<>() {{
    put("zero", 0);
    put("one", 1);
}};

Accessing values:

A value is retrieved from a map using the get() method:

Map<string, int> digitValue = new Dictionary<>() {{
    put("zero", 0);
    put("one", 1);
}};

// Accesing values:
digitValue.get("zero"); // returns  0

digitValue.get("one"); //  returns 1

digitValue.get("three"); // raises an exception since 'three' does not exist

digitValue.getOrDefault("three", 0); // returns 0 as the default value

As you can see, if you refer to a key that is not in the map, Java raises an exception. We can use getOrDefault to specify a default value in case it does not exist

Accessing a value from a map takes O(1) time.


Adding an entry:

We can add a key / value pair by using the put() method as follows:

Map<string, int> digitValue = new Dictionary<>() {{
    put("zero", 0);
    put("one", 1);
}};

// Adding new entries:
digitValue.put("three", 3);

//The new Hash Map:
// {
//	"zero": 0,
//  "one":  1,
//	"three": 3
//}

Adding a new entry to a map takes O(1) time.


Updating an entry:

We can update an existing entry by using the put() method as follows:

Map<string, int> digitValue = new Dictionary<>() {{
    put("zero", 0);
    put("one", 1);
}};

// updating entries:
digitValue.put("zero", 5);

//the new hash map:
// {
//	"zero": 5,
//  "one":  1
//}

Updating an entry from a map takes O(1) time.


Deleting entries:

The remove() method allows you to remove a key from a map:

Map<string, int> digitValue = new Dictionary<>() {{
    put("zero", 0);
    put("one", 1);
}};

// removing entries:
digitValue.Remove("zero");

//the new hash map:
// {
//  "one":  1
//}

If you try to delete a key that is not in the map, it won't do anything.

Deleting an entry from a map takes O(1) time.


Checking if a key exists:

You can check if a key exists in a map using the containsKey() method:

Map<string, int> digitValue = new Dictionary<>() {{
    put("zero", 0);
    put("one", 1);
}};


digitValue.containsKey("zero"); // returns true since the key exists

digitValue.containsKey("three"); // returns false since the key does not exist

Checking if a key exists in the map is O(1) time.


Iterating over the map keys:

If we want to iterate over keys of the map, we can use the for loop along with the : operator and the keySet() method as follows:

Map<string, int> digitValue = new Dictionary<>() {{
    put("zero", 0);
    put("one", 1);
}};

for (string key : digitValue.keySet()) {
    Console.WriteLine(key + " is " + digitValue.get(key));
}

// This will print:
// one is 1
// zero is 0

Notice the order is not the same as initiated. Dictionary keeps the data in random order

Iterating over a dictionary takes O(n) time.



Assignment
Follow the Coding Tutorial and let's play with some dictionaries.


Hint
Look at the examples above if you get stuck.


STEP 1 OF 5
    Ctrl + Enter to check

    C# · HASH TABLE

    Hash Map in C#, explained

    What a hash map is

    A hash map stores key-value pairs. You hand it a key and it gives back the value associated with that key, without scanning through everything it holds. Keys are unique: each key maps to exactly one value, while different keys may share a value.

    In C#, the hash map is Dictionary<TKey, TValue> from System.Collections.Generic. The two type arguments are the type of the keys and the type of the values. A Dictionary<string, int> maps strings to integers, for example "two" to 2. Looking up, adding, updating, removing and testing a key all take O(1) time on average, which is why a dictionary is the standard tool for counting, caching and "have I seen this before?" problems.

    Creating a dictionary

    An empty dictionary is created with new and the two types:

    Dictionary<string, int> digitValue = new Dictionary<string, int>();
    

    You can also fill it at creation with a collection initializer. Each pair goes in its own braces:

    Dictionary<string, int> digitValue = new Dictionary<string, int> {
        {"zero", 0},
        {"one", 1}
    };
    

    An equivalent index-style initializer is new Dictionary<string, int> { ["zero"] = 0, ["one"] = 1 }. Creating an empty dictionary takes O(1) time.

    Reading values

    The square-bracket indexer reads a value by key:

    int zero = digitValue["zero"];  // 0
    int three = digitValue["three"]; // throws KeyNotFoundException
    

    A missing key throws KeyNotFoundException, so the indexer is only safe when you know the key is present. There are three safer options:

    • ContainsKey(key) returns true or false, so you can test before you read.
    • GetValueOrDefault(key, fallback) returns the stored value, or fallback when the key is missing. With one argument, the fallback is the type's default, which is 0 for int.
    • TryGetValue(key, out value) returns a bool and writes the value into an out variable: if (digitValue.TryGetValue("one", out int v)) { ... }.

    Each lookup is O(1).

    Adding, updating and removing

    Assigning through the indexer adds a key that is not there and replaces the value of a key that is:

    digitValue["three"] = 3;  // adds
    digitValue["zero"] = 5;   // updates
    digitValue.Remove("one"); // removes
    

    The Add(key, value) method is stricter: it throws ArgumentException if the key already exists. TryAdd(key, value) adds only when the key is new and returns whether it did, so an existing entry is left alone. Remove(key) returns true if it removed something and false if the key was not present, and it never throws for a missing key. All three operations are O(1).

    Iterating

    A foreach over the dictionary visits KeyValuePair objects, with Key and Value properties. You can also loop over just dict.Keys or dict.Values:

    foreach (var pair in digitValue) {
        Console.WriteLine(pair.Key + " is " + pair.Value);
    }
    

    Iteration takes O(n) time. Do not rely on the order. A dictionary comes with no guarantee about it, even though a dictionary that has only ever had items added often lists them in insertion order. In a test run, a dictionary holding apples, bananas and grapes had bananas removed and then kiwi added; iteration gave apples, kiwi, grapes, because kiwi reused the slot bananas had freed.

    Adding a new key to the dictionary while you loop over it throws InvalidOperationException. If you need to add entries while looping, loop over a copy such as dict.Keys.ToList() (this needs using System.Linq;).

    Common mistakes

    • Reading a missing key with the indexer. The result is an exception, not a default value. Use GetValueOrDefault or TryGetValue for counting patterns.
    • Calling Add twice for the same key. It throws. Use the indexer to overwrite, or TryAdd to keep the first value.
    • Wrong method names. The C# members are ContainsKey, Remove, Add and Keys, all in PascalCase. Names such as put, get or containsKey and the type Map are not part of Dictionary and do not compile.
    • Using null as a key. A dictionary does not accept a null key and throws ArgumentNullException.
    • Expecting a fixed order when printing the contents, as covered above.

    The exercise

    The lesson tracks how many fruits of each kind you have and walks through the operations above.

    1. Create fruits as a Dictionary<string, int> initialized with ("apples", 3), ("bananas", 5) and ("grapes", 2).
    2. Add four more apples. Read the current count with GetValueOrDefault("apples", 0), add 4, and assign the sum back through the indexer.
    3. Report a missing fruit. If ContainsKey("pears") is false, print Out of pears.
    4. Remove the bananas with Remove("bananas").
    5. Print every fruit. Loop with foreach (string fruit in fruits.Keys) and print We have, the count from fruits[fruit], and the fruit's name.
    Dictionary<string, int> fruits = new Dictionary<string, int> {
        {"apples", 3},
        {"bananas", 5},
        {"grapes", 2}
    };
    
    fruits["apples"] = fruits.GetValueOrDefault("apples", 0) + 4;
    
    if (!fruits.ContainsKey("pears")) {
        Console.WriteLine("Out of pears");
    }
    
    fruits.Remove("bananas");
    
    foreach (string fruit in fruits.Keys) {
        Console.WriteLine("We have " + fruits[fruit] + " " + fruit + ".");
    }
    

    Running it prints:

    Out of pears
    We have 7 apples.
    We have 2 grapes.
    

    The lesson accepts the two fruit lines in either order, because a dictionary makes no promise about order.

    Key takeaways

    • Dictionary<TKey, TValue> is C#'s hash map: unique keys, one value per key, and O(1) average operations.
    • The indexer reads, adds and updates, but reading a missing key throws KeyNotFoundException.
    • ContainsKey, TryGetValue and GetValueOrDefault are the safe ways to look up a key that might be absent.
    • Remove is safe on missing keys, and Add is not safe on existing ones.
    • Iterate with foreach over the pairs or Keys, and never depend on the order.
    Keep going with C#About 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free