C# Dictionary ContainsValue: Usage and Performance
c# dictionary containsvalue: Learn how Dictionary.ContainsValue works, its O(n) complexity, equality rules, and when to use it instead of ContainsKey or alternative st...
c# dictionary containsvalue requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.
When you need to check whether a value exists in a C# Dictionary, the ContainsValue method is the direct answer. Unlike ContainsKey, which performs an O(1) hash-based lookup, ContainsValue scans every entry until it finds a match. That distinction matters for both correctness and performance, especially as the dictionary grows.
What ContainsValue Does and When to Use It
Dictionary<TKey, TValue>.ContainsValue returns true if any key in the dictionary maps to the specified value. It uses the default equality comparer for the value type unless you provide a custom comparer when constructing the dictionary. This method is useful when you need to validate that a value is present without knowing which key it belongs to, or when you want to avoid storing a separate set of values.
For example, you might use it to check whether a user has already been assigned a particular email address before inserting a new entry. If the email is the value and the user ID is the key, ContainsValue gives you that check in one call.
Syntax and Basic Usage
The method signature is simple:
public bool ContainsValue(TValue value)
Here is a minimal example:
var scores = new Dictionary<string, int> { ["alice"] = 90, ["bob"] = 85, ["carol"] = 92 }; bool hasPerfectScore = scores.ContainsValue(92); // true bool hasScore100 = scores.ContainsValue(100); // false
The method returns false if the dictionary is empty. It also returns false if the value is null and no entry has a null value, because the default equality comparer treats null as a valid value.
How Value Equality Is Determined
ContainsValue relies on the equality comparer associated with the dictionary. By default, that is EqualityComparer<TValue>.Default. For primitive types and strings, this performs an appropriate value comparison. For custom classes, it uses the Equals method unless the class overrides GetHashCode and Equals consistently.
If you create the dictionary with a custom IEqualityComparer<TValue>, that comparer is used for value lookups. This is important when values are objects whose equality is not reference-based. For instance:
public record Person(string Name, int Age); var people = new Dictionary<int, Person>(); people.Add(1, new Person("Alice", 30)); bool hasAlice = people.ContainsValue(new Person("Alice", 30)); // true for records
Because records implement value-based equality, the new Person instance matches the stored one. For a regular class without overridden equality, this would return false because reference equality is used.
Performance Characteristics of ContainsValue
The critical difference between ContainsKey and ContainsValue is algorithmic complexity. ContainsKey uses the hash code of the key to locate a bucket in O(1) average time. ContainsValue has no such shortcut; it must enumerate all key-value pairs and compare each value. This is an O(n) operation, where n is the number of entries in the dictionary.
| Method | Complexity | Typical Use Case |
|---|---|---|
ContainsKey | O(1) | Check for a key before updating |
ContainsValue | O(n) | Check for a value when key is unknown |
This means that for a dictionary with thousands of entries, ContainsValue can become a bottleneck if called frequently. The cost grows linearly with the dictionary size. If you find yourself calling ContainsValue in a loop, consider whether you can invert the data structure or maintain a separate lookup index.
When to Avoid ContainsValue
If your primary operation is checking for value existence and you do not need the key-value mapping, a HashSet<T> may be a better choice. HashSet<T>.Contains also runs in O(1) average time. For example, if you only need to track a set of unique email addresses, a HashSet<string> is more efficient than a Dictionary<int, string> with ContainsValue.
Another scenario is when you need to find the key associated with a value. ContainsValue only tells you whether the value exists; it does not give you the key. To retrieve the key, you must iterate manually:
var key = scores.FirstOrDefault(kvp => kvp.Value == 92).Key;
This is also O(n), but it gives you the key. If you need this kind of reverse lookup frequently, consider maintaining a second dictionary that maps values to keys, or use a bidirectional map implementation.
Working with Custom Types and Equality
When your dictionary values are custom types, the behavior of ContainsValue depends on how equality is defined. If you have not overridden Equals and GetHashCode, the default reference equality means that two distinct objects with identical property values are not considered equal. This often surprises developers.
Consider a class without overridden equality:
public class Product { public string Name { get; set; } public decimal Price { get; set; } } var products = new Dictionary<int, Product>(); products.Add(1, new Product { Name = "Laptop", Price = 999m }); bool exists = products.ContainsValue(new Product { Name = "Laptop", Price = 999m }); // false
To make this work, you can override Equals and GetHashCode in the Product class, or you can pass a custom IEqualityComparer<Product> to the dictionary constructor. The latter is useful when you cannot modify the class or when equality rules vary by context.
var comparer = new ProductComparer(); // implements IEqualityComparer<Product> var products = new Dictionary<int, Product>(comparer);
Keep in mind that the comparer is used for both keys and values. If you need different equality rules for keys and values, you cannot use a single comparer for both. In that case, you may need to maintain a separate HashSet<TValue> with its own comparer.
Practical Example: Finding Keys by Value
A common need is to locate all keys that map to a particular value. ContainsValue alone does not provide this, but you can combine it with iteration. Here is a method that returns all keys for a given value:
public static IEnumerable<TKey> FindKeysByValue<TKey, TValue>( Dictionary<TKey, TValue> dictionary, TValue value) { foreach (var kvp in dictionary) { if (EqualityComparer<TValue>.Default.Equals(kvp.Value, value)) { yield return kvp.Key; } } }
This approach is O(n) and works for any dictionary. If you need this operation frequently, consider maintaining an inverse mapping. For example, if you have a dictionary mapping user IDs to email addresses, you could also keep a dictionary mapping email addresses to user IDs. The extra memory is often worth the O(1) lookup speed.
When you do use ContainsValue, be aware of the equality comparer behavior. If the dictionary was constructed with a custom comparer, the ContainsValue method uses that comparer, not the default. This is consistent with how ContainsKey works, but it is easy to forget when the value type is a reference type.
In summary, ContainsValue is a straightforward method that serves a specific purpose. Use it when you need a simple existence check and the dictionary is small or the operation is infrequent. For performance-sensitive code, evaluate whether an alternative data structure or an inverse index better fits your access pattern.