1

A bit of a rudimentary question, but here goes...

I often use dictionaries to store ancillary information about other objects:

Dictionary<Person, int> timesAccessed;

Person p = // ...;
if (timesAccessed[p] == 0)
    // ...

Let's say my 'Person' class just looks like:

class Person
{
    public string Name;
}

Note that I haven't implemented Equals, GetHashCode, etc. on Person. Is the dictionary still able to do fast lookups with a key like this? Since Person is a class, and p is reference, I can imagine it using the memory address / some internal index as the hash code, but I'd like to be sure that it isn't resorting to a linear scan of the dictionary.

Mad Dog Tannen
  • 7,129
  • 5
  • 31
  • 55
Barguast
  • 5,926
  • 9
  • 43
  • 73
  • 1
    It's easy to test: create some Person objects and call GetHashCode() on them yourself. They will be different. – fejesjoco Nov 29 '13 at 10:47
  • It's the OPPOSITE of what @gdoron says. All the instances will give separate hash codes (EVEN IF `Name` has the same value - which is one of the things which may make the default generated hash code a bad choice, if you want to treat objects containing the same data as equal) – Matthew Watson Nov 29 '13 at 11:23

1 Answers1

2

Even the retrieval of Dictionary items is a game of luck without the implementation of Equals or an IEqualityComparer. It will work as long as you ask for exactly the same instance but if you ask for a new instance with the same data as an item in the dictionary, the dictionary will not find the item.
So if you want a deterministic behavior of the dictionary, you either have to implement Equals and GetHashCode on the classes themselves. As an alternative, aforementioned IEqualityComparer is an option that you can also use if you cannot change the classes that are used as key or if you want to implement different rules for comparison:

private class PersonEqualityComparer : IEqualityComparer<Person>
{
    public bool Equals(Person p1, Person p2)
    {
        // Compare the strings as needed (culture, casing, nulls, ...)
        return p1.Name == p2.Name;
    }

    public int GetHashCode(Person p)
    {
        // Handle nulls as needed
        return p.Name.GetHashCode();
    }
}

When creating the dictionary, you can provide the equality comparer in the constructor:

var dict = new Dictionary<Person, int>(new PersonEqualityComparer());

The interface also requires the implementation of GetHashCode for the target type, so I expect the dictionary to use this for finding items effectively.

Markus
  • 20,838
  • 4
  • 31
  • 55
  • I should've been more clear - what I'm wanting to do is a lookup purely based on the reference rather than the value of the object. If I create two 'Person' objects with the same name, I should be able to add these both to my dictionary as two separate entries. The way I demonstrated allows this since it seems to use reference equality by default. However, since this is a dictionary, I'm hoping it can still do O(1) lookups despite me not implementing GetHashCode. – Barguast Nov 29 '13 at 12:16
  • @Barguast Yes, it will work OK. See this thread for details: ckoverflow.com/questions/720177/default-implementation-for-object-gethashcode – Matthew Watson Nov 29 '13 at 14:59