Lazlo
Lazlo

Reputation: 8790

Dictionary does not contain key despite Comparer.Equals returning true

I've been hunting down a bug where I have a dictionary that insists on saying a key does not exist, despite its comparer actually saying that it does. For example, in the following snippet, the exception is thrown:

if (!dictionary.ContainsKey(key))
{
    var comparer = dictionary.Comparer;

    foreach (var _key in dictionary.Keys)
    {
        if (comparer.Equals(key, _key) &&
            comparer.Equals(_key, key) &&
            comparer.GetHashCode(key) == comparer.GetHashCode(_key) &&
            comparer.GetHashCode(_key) == comparer.GetHashCode(key))
        {
            throw new Exception("Key exists, but dictionary doesn't find it");
        }
    }
}

The dictionary is a generic Dictionary<TKey, TValue> with the default equality comparer (empty constructor). The TKey class implements proper GetHashCode and Equals methods.

Is there anything I might be missing here? I'm at a complete loss!

Upvotes: 3

Views: 467

Answers (1)

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726879

The only way this could happen is if the key is mutable, and its content has been changed after it has been inserted into the dictionary.

Here is how it happens:

  • You insert a key into a dictionary; the dictionary gets its hash code, and puts it into a corresponding hash bucket, resolving conflicts by calling Equals
  • You mutate the key; now its hash code no longer corresponds to its hash bucket
  • You call ContainsKey. The dictionary looks for the key in its new bucket. Since the key is not there, false is reported
  • When you iterate the dictionary, all keys are returned, including ones that are no longer in their legitimate buckets. That's why you get a hit in the foreach loop.

Upvotes: 8

Related Questions