3

I've written a class deriving from IEqualityComparer<T> which works great for the LINQ query I needed it for.

As I understand it, GetHashCode() (fast) is called first, then Equals()(slightly slower) if the hashcode is the same, for such operations.

However when using it for direct comparisons, manually, I'm using something like

return new MyIEqualityComparer().Equals(objA,objB);

Which forgoes the faster GetHashCode() equality check. Is there a way of comparing objA to objB which doesn't automatically skip the faster GetHashCode() check?

I guess I was hoping objA.Equals() would have an overload that accepted an argument derived from IEqualityComparer<T>.

maxp
  • 24,209
  • 39
  • 123
  • 201
  • 1
    Do you have a performance problem you are trying to solve, or is this just 'optimization'? – stuartd Jun 09 '16 at 11:36
  • GetHashCode doesn't prove equality, it lets you find which bucket an item belongs to in a hash table (Dictionary or HashSet), so it can do an actual equality check on much fewer items instead of the whole collection. – Bryce Wagner Jun 09 '16 at 11:38
  • Its primary purpose is for putting objects in a hash table, I don't think you can take any guarantees on whether it's faster or not. – Charles Mager Jun 09 '16 at 11:39
  • No performance problem, but I'd be surprised if I have to manually write `return comparer.GetHashCode(objA)==comparer.GetHasCode(objB)?comparer.Equals(objA,objB):false`... – maxp Jun 09 '16 at 11:39
  • @BryceWagner but it *does* prove inequality. Any it should always be written such that it is faster than `Equals()` – maxp Jun 09 '16 at 11:42
  • 4
    The Equals comparison for non-trivial cases should actually be cheaper to call than GetHashCode. If you check two strings are equal, it can shortcut out if the lengths are different, but if you compare hash codes, it has to hash the entire string first. – Bryce Wagner Jun 09 '16 at 11:42
  • 1
    `Equals` does not have to be slower. Where did you get that from? I suspect you read about some kind of Dictionary structure that explain how hashing can dramatically improve access (locating bucket, then use `Equals` on each item in that bucket), but this gain does not automatically transpose when just comparing two objects. Using `GetHashCode` on both objects when overloading equality _can_ be faster (to shortcut on inequality only, of course) if the objects are particularly slow to compare but can be hashed rapidly. This does not occur often in practice, to say the least. – Patrice Gahide Jun 09 '16 at 12:23
  • If you want to override equals and put a hash check in it the do so. A hash check is not always faster. Even is it was twice as fast it is still 50% overhead in the case the hash does match. – paparazzo Jun 09 '16 at 12:37
  • BTW, if you want a *really* fast equality compare, and also a faster GetHashCode, then *store* the hash code as a field in the object itself, when you create the object. I don't recall the exact SO thread, but there is a test that shows even the dumbest cached `HashCode` - a 32-bit ID that you increment with each object creation - doubles performance of dictionary lookup for a custom object. And note that as long as you create less than 2**32 such objects in one app session, both Equals and GetHashCode are trivial, and essentially instantaneous. Memory vs performance. – ToolmakerSteve Mar 01 '18 at 00:16

2 Answers2

2

Computing a hash code and comparing the hash generally is slower than comparing for equality directly. It's additional work.

Hash codes are there to support the O(1) behavior of hash tables. They map an object to a number which is required for hash tables to work. Hash codes are not helpful for mere equality comparisons.

Just use Equals.

If you want to know how to best implement your idea (although it is not a good idea) I'd use a helper method:

static bool ExperimentalEquals<T>(T a, T b, IEqualityComparer<T> c) {
 if (c.GetHashCode(a) != c.GetHashCode(b)) return false;
 return c.Equals(a, b);
}

(For educational purposes only.)

You might think that in case of a cached hash code this actually could be faster. But then Equals could make use of the cached hash code itself and short circuit the comparison.

usr
  • 168,620
  • 35
  • 240
  • 369
0

It's not really clear what you're up to here, but you can always implement IEquatable<T> and delegate to MyIEqualityComparer, thus using faster GetHashCode():

class My : IEquatable<My>
{
    public bool Equals(My other)
    {
        return new MyIEqualityComparer().Equals(this, other);
    }
}
Anton Gogolev
  • 113,561
  • 39
  • 200
  • 288