I need to figure out if what I’m doing is a bad/incorrect habit, and why.
I’ve been using objects derived from MonoBehaviours, C# Interfaces and UnityEngine.Object as keys in Dictionary<,> objects and in HashSet<> collections. The goal is fast access from a collection of potentially hundreds of objects. Rarely thousands.
I don’t explicitly override GetHashCode() or Equals() for any of these, and it seems to be “working fine” as is. However, is it possible that adding/querying for an object could generated a false positive? Two separate objects instances added to a HashSet or Dictionary would generate the same key and cause an issue?
When I use an object as a key that doesn’t override GetHashCode() or Equals(), what is it using as the key? Is there something about this system I’m missing completely? Thanks.
Even if you had two reference types which produced the same hash code (the same key), it wouldn’t be a game-stopping issue for a dictionary. Normally, when everything is perfect a dictionary is a collection of one key to one value. Each hashed key value will point to only one value. This is nice and perfect.
When two objects return the same hash code, the dictionary will create a collection of objects and iterate over that collection to find the key that exactly matches the referenced key. This is slower than a perfect one-to-one hash lookup, but its still as efficient as things can get.
The internals of a dictionary are a bit too complicated for a quick post. The short of it is that the dictionary can handle hash collisions by iterating over a small list to return the proper value for a reference key.
If you’re inheriting from MonoBehaviour, you’re inheriting from UnityEngine.Object. Object’s GetHashCode is really fast - it’s just their instanceID, so it’s a direct lookup, and it won’t have collisions for objects unless both objects have been destroyed. Equals is a bit slow due to Unity’s special-casing of destroyed == null that needs to be checked.
If you’re doing very, very many dictionary lookups and you’re seeing them in the profiler, then look into implementing IEquatable and such so the Dictionary can do the fast path for some things. Otherwise the default performance should be more than enough.