What Is Dictionary<TKey, TValue> in C#?
Dictionary<TKey, TValue> is a generic collection in C#
that stores data as key-value pairs. Each key identifies
a corresponding value, allowing applications to retrieve information
efficiently using the key instead of searching through every element.
A dictionary is commonly used for lookups such as finding a user by ID, retrieving a product by its code, mapping configuration names to values, counting occurrences, and associating one piece of information with another.
Key-Value Pair Structure
Every dictionary entry consists of a key and a value. The key must be unique within the dictionary, while multiple keys can have values that are equal.
Key Value
-------------------
101 "Asha"
102 "Rahul"
103 "Priya"
In this example, 101, 102, and 103
are keys and the corresponding names are values.
Creating a Dictionary in C#
The generic dictionary is available through the
System.Collections.Generic namespace.
using System.Collections.Generic;
Dictionary<int, string> users =
new Dictionary<int, string>();
The first type parameter specifies the key type and the second specifies
the value type. Here, the dictionary uses int keys and
string values.
Initializing a Dictionary with Values
A dictionary can be initialized with key-value pairs when it is created.
var users = new Dictionary<int, string>
{
{ 101, "Asha" },
{ 102, "Rahul" },
{ 103, "Priya" }
};
Console.WriteLine(users[102]);
// Rahul
Adding Elements with Add()
The Add() method inserts a new key-value pair into the
dictionary. The key must not already exist.
var scores = new Dictionary<string, int>();
scores.Add("Asha", 95);
scores.Add("Rahul", 88);
scores.Add("Priya", 91);
Console.WriteLine(scores["Asha"]);
// 95
Attempting to add another entry with an existing key using
Add() throws an ArgumentException.
Adding or Updating with the Indexer
The dictionary indexer can both add a new key and update an existing key. This makes it useful when you do not need to distinguish between the two operations.
var scores = new Dictionary<string, int>();
scores["Asha"] = 95;
scores["Rahul"] = 88;
// Update existing value
scores["Asha"] = 98;
Console.WriteLine(scores["Asha"]);
// 98
Retrieving a Value by Key
A value can be retrieved using its key with the dictionary indexer.
var products = new Dictionary<int, string>
{
{ 1001, "Laptop" },
{ 1002, "Keyboard" },
{ 1003, "Mouse" }
};
string product = products[1002];
Console.WriteLine(product);
// Keyboard
If the requested key does not exist, accessing it through the indexer
throws a KeyNotFoundException.
Safe Lookup with TryGetValue()
When a key may not exist, TryGetValue() is often the preferred
lookup pattern. It returns true when the key exists and
places the corresponding value in the output variable.
var users = new Dictionary<int, string>
{
{ 101, "Asha" },
{ 102, "Rahul" }
};
if (users.TryGetValue(102, out string? name))
{
Console.WriteLine(name);
}
else
{
Console.WriteLine("User not found");
}
This avoids performing a separate lookup followed by another indexed access and also avoids an exception when the key is missing.
Checking Whether a Key Exists
Use ContainsKey() when you only need to know whether a key is
present.
var users = new Dictionary<int, string>
{
{ 101, "Asha" },
{ 102, "Rahul" }
};
Console.WriteLine(users.ContainsKey(101));
// True
Console.WriteLine(users.ContainsKey(999));
// False
Checking for a Value
ContainsValue() checks whether at least one dictionary entry
contains a specified value.
var users = new Dictionary<int, string>
{
{ 101, "Asha" },
{ 102, "Rahul" }
};
Console.WriteLine(users.ContainsValue("Rahul"));
// True
Unlike key lookup, checking for a value generally requires examining the entries and therefore has linear complexity.
Removing Entries
The Remove() method removes an entry using its key. It
returns true when the entry was removed successfully.
var users = new Dictionary<int, string>
{
{ 101, "Asha" },
{ 102, "Rahul" },
{ 103, "Priya" }
};
bool removed = users.Remove(102);
Console.WriteLine(removed);
// True
Console.WriteLine(users.Count);
// 2
Clearing a Dictionary
The Clear() method removes all key-value pairs from the
dictionary.
var scores = new Dictionary<string, int>
{
{ "Asha", 95 },
{ "Rahul", 88 }
};
scores.Clear();
Console.WriteLine(scores.Count);
// 0
Iterating Through a Dictionary
A dictionary can be traversed using foreach. Each iteration
provides a KeyValuePair<TKey, TValue>.
var users = new Dictionary<int, string>
{
{ 101, "Asha" },
{ 102, "Rahul" },
{ 103, "Priya" }
};
foreach (var user in users)
{
Console.WriteLine($"ID: {user.Key}, Name: {user.Value}");
}
Accessing Keys and Values
The Keys and Values properties provide
collections that can be used when only one side of the dictionary is
required.
var scores = new Dictionary<string, int>
{
{ "Asha", 95 },
{ "Rahul", 88 }
};
foreach (var name in scores.Keys)
{
Console.WriteLine(name);
}
foreach (var score in scores.Values)
{
Console.WriteLine(score);
}
Dictionary Count
The Count property returns the number of key-value pairs
currently stored.
var users = new Dictionary<int, string>
{
{ 1, "Asha" },
{ 2, "Rahul" },
{ 3, "Priya" }
};
Console.WriteLine(users.Count);
// 3
Dictionary with Custom Objects
Dictionaries become particularly useful when an application needs to retrieve objects using an identifier.
class Product
{
public int Id { get; set; }
public string Name { get; set; } = "";
public decimal Price { get; set; }
}
var products = new Dictionary<int, Product>
{
{
101,
new Product
{
Id = 101,
Name = "Laptop",
Price = 75000
}
},
{
102,
new Product
{
Id = 102,
Name = "Keyboard",
Price = 2500
}
}
};
if (products.TryGetValue(101, out Product? product))
{
Console.WriteLine(product.Name);
Console.WriteLine(product.Price);
}
Dictionary with String Keys
String keys are commonly used for configuration values, labels, user preferences, and other named data.
var configuration = new Dictionary<string, string>
{
{ "Environment", "Production" },
{ "Region", "Asia" },
{ "Version", "2.0" }
};
Console.WriteLine(configuration["Environment"]);
// Production
Counting Frequencies with a Dictionary
A dictionary is commonly used to count how frequently values occur. The key represents the value being counted and the dictionary value stores its current frequency.
var numbers = new[] { 1, 2, 1, 3, 2, 1 };
var frequency = new Dictionary<int, int>();
foreach (var number in numbers)
{
if (frequency.ContainsKey(number))
{
frequency[number]++;
}
else
{
frequency[number] = 1;
}
}
foreach (var item in frequency)
{
Console.WriteLine($"{item.Key}: {item.Value}");
}
Using TryGetValue for Frequency Counting
TryGetValue() can make frequency-counting code concise while
avoiding an exception for a missing key.
var words = new[] { "C#", "Java", "C#", "Python", "Java", "C#" };
var frequency = new Dictionary<string, int>();
foreach (var word in words)
{
if (frequency.TryGetValue(word, out int count))
{
frequency[word] = count + 1;
}
else
{
frequency[word] = 1;
}
}
foreach (var item in frequency)
{
Console.WriteLine($"{item.Key}: {item.Value}");
}
Using Collections as Dictionary Values
The value type of a dictionary can itself be a collection. This is useful for representing one-to-many relationships.
var skillsByDeveloper = new Dictionary<string, List<string>>
{
{
"Asha",
new List<string> { "C#", "SQL", "Azure" }
},
{
"Rahul",
new List<string> { "Java", "Spring", "Docker" }
}
};
foreach (var skill in skillsByDeveloper["Asha"])
{
Console.WriteLine(skill);
}
Dictionary and LINQ
LINQ can be used to filter and transform dictionary entries. The dictionary can be treated as a sequence of key-value pairs.
using System.Linq;
var scores = new Dictionary<string, int>
{
{ "Asha", 95 },
{ "Rahul", 72 },
{ "Priya", 88 },
{ "Arun", 64 }
};
var highScores = scores
.Where(item => item.Value >= 80)
.ToDictionary(item => item.Key, item => item.Value);
foreach (var item in highScores)
{
Console.WriteLine($"{item.Key}: {item.Value}");
}
Dictionary Time Complexity
A dictionary is implemented using hashing. Under normal conditions, key insertion, lookup, and removal are expected to be efficient.
| Operation | Typical Complexity | Notes |
|---|---|---|
| Lookup by key | O(1) average | Hash-based lookup |
| Add | O(1) average | May occasionally require resizing |
| Update by key | O(1) average | Requires locating the key |
| Remove by key | O(1) average | Hash-based lookup |
| ContainsKey | O(1) average | Checks the key |
| ContainsValue | O(n) | May inspect every entry |
| Iteration | O(n) | Visits every entry |
Dictionary Keys Must Be Unique
A dictionary cannot contain two entries with the same key. If the
application needs multiple values associated with one key, the value can
instead be a collection such as List<T>.
var coursesByStudent = new Dictionary<int, List<string>>
{
{
101,
new List<string> { "C#", "SQL" }
},
{
102,
new List<string> { "Java", "Spring" }
}
};
coursesByStudent[101].Add("Azure");
Console.WriteLine(
string.Join(", ", coursesByStudent[101])
);
// C#, SQL, Azure
Choosing a Dictionary Key
A dictionary key should represent a stable identifier that can reliably distinguish one entry from another. Common keys include numeric IDs, strings, enums, and other types with suitable equality and hashing behavior.
For custom key types, correct equality and hash-code behavior is especially important because the dictionary relies on those operations to locate entries.
Dictionary vs List<T>
A List<T> and a dictionary solve different access
problems. A list is organized around position, while a dictionary is
organized around keys.
| Feature | List<T> | Dictionary<TKey, TValue> |
|---|---|---|
| Primary access | Index | Key |
| Lookup | O(n) generally | O(1) average by key |
| Duplicate values | Allowed | Allowed |
| Duplicate keys | Not applicable | Not allowed |
| Best suited for | Ordered sequential data | Key-based lookup |
Common Dictionary Mistakes
- Trying to add the same key more than once with
Add(). - Accessing a key with the indexer without checking whether it exists.
- Using
ContainsValue()when the application actually needs key-based lookup. - Assuming dictionary entries are accessed by numeric index like a list.
- Using mutable objects as keys without understanding equality and hashing behavior.
- Choosing a dictionary when the application primarily requires sequential ordering and index access.
Practical Example: Product Lookup
A product catalog can use a dictionary to associate product IDs with product objects. This avoids scanning a complete list whenever a product needs to be retrieved by ID.
record Product(int Id, string Name, decimal Price);
var products = new Dictionary<int, Product>
{
{ 1001, new Product(1001, "Laptop", 75000) },
{ 1002, new Product(1002, "Keyboard", 2500) },
{ 1003, new Product(1003, "Mouse", 1200) }
};
if (products.TryGetValue(1002, out Product? product))
{
Console.WriteLine(product.Name);
Console.WriteLine($"Price: {product.Price}");
}
Key Takeaways
Dictionary<TKey, TValue>stores data as key-value pairs.- Every key must be unique within the dictionary.
Add()inserts a new key-value pair and rejects duplicate keys.- The indexer can add a new key or update an existing value.
TryGetValue()provides a safe way to retrieve values when a key may not exist.ContainsKey()checks whether a key is present.- Lookup, insertion, and removal are typically O(1) on average.
ContainsValue()generally requires O(n) time.- Dictionaries are useful for fast key-based lookups, mappings, and frequency counting.