2

I would like to implement some geometrical algorithms with numerical robustness.

For this, a system-wide delta is used for geometric equality. equals() for points is implemented with a distance computation using the delta for approximate equality.

I would like to be able to use regular java Collections, e.g Set. But I can't come up with a reasonable hashCode() implementation.

My guess is that an implementation for an efficient HashSet use would result in a space partitioning with "soft" borders. Points with distance less that delta to the partition border should be able to be classified in up to eight (for 3D) adjacent regions at the same time. Points close enough to be considered equal on behalf of their distance but lying on different sides of the partition would otherwise be "misclassified".

This is what I can't get my head around. hashCode() is like putting items in buckets with a single item ending up in a single bucket, while I would need to put it in up to eight.

What would be a resonable solution? Am I abusing the purpose of hashCode()? And what would be the most reasonable solution still using hashCode() :)

EDIT: Thank you, I had an intuition that there was something wrong with the idea but could not put my finger on it. You made the matter very clear

Please let me extend my question to : if I'm fine with slower HashSet operation (which is not a showstopper), I could have hashCode() return 1 as there is no correct implementation in my case, what dire consequences would there be (it terms of geometric computations), if I do implement equals() dropping the transitivity requirement?

EDIT I have found this post, highlighting the issues with missing transitivity and and this post, which is closely related to this one.

Community
  • 1
  • 1
kostja
  • 60,521
  • 48
  • 179
  • 224

3 Answers3

5

This is fundamentally impossible.

equals() / hashCode() -based equality can only be defined on mathematical equivalence relations that obey three rules:

  • a ~ a. (Reflexivity)
  • if a ~ b then b ~ a. (Symmetry)
  • if a ~ b and b ~ c then a ~ c. (Transitivity)

Your definition is not transitive, so you cannot use it.
Instead, you need an alternate data structure.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
5

hashCode() can be equal for un-equals() objects. So, indeed, bucketing is probably just fine. For example, if you just use some hash function of the nearest "grid" point, where you can define that grid however you want, it will work as a hash function as long as you define the rounding consistently and correctly.

However, you are not going to get a correct notion of equals() from your first definition. If A and B, and B and C, are within delta of one another, this does not mean that A and C are. Transitivity does not hold.

You could define equals() in terms of bucketing. It may give slightly surprising results as points close but on the other side of a line are not equal.

Sean Owen
  • 66,182
  • 23
  • 141
  • 173
3

I would recommend against this entire approach. For one thing, it will break the transitive property of equals. (If delta == 0.1, then 1 == 1.1 and 1.1 == 1.2, but 1 != 1.2.) No hashcode implementation is going to fix that. It's also likely to make a mess of the collections framework in general.

Ted Hopp
  • 232,168
  • 48
  • 399
  • 521