Re: equals(), Sets, Maps, and degrees of equality
On 11/9/2011 9:33 PM, Sean Mitchell wrote:
Anyone ever run into the case where you wish an Object could have more than one equals(), or that Set and Map implementations would let you pass in something like a closure to determine key equality?
IIRC, Common Lisp supports four or five different notions of
"equality." So, yes: The idea of equivalences beyond Java's two has
in fact been deemed useful by someone.
It seems to me that objects can be equal in varying degrees. Let's consider a class Dog:
public class Dog {
String breed;
String name;
String age;
}
I may want to have a Set<Dog>, which holds only one Dog of each breed, irrespective of name of age. In this case my equals()/hashcode() would only consider breed.
Seems odd: Why should Fido rather than Rover or Wossname be the
sole representative Cocker Spaniel? Or, why do you want a set of Dogs,
rather than a set of Breeds? Even then I think you'd have difficulty:
Are Schnauzer, Miniature Schnauzer, and Standard Schnauzer one breed or
three?
But okay -- Let's not dwell on the quirks of the example: We'll
suppose that you've got a bunch of objects with multiple attributes,
and you sometimes want to think of them equivalent if their X's match,
while other times you want to pay attention only to their Y's.
But I may also want a Mag<Dog, Owner> in which each Dog is made unique by name.
And of course, there is the most intuitive case where I want to use equals() to see if the instances map on all three fields.
I suppose I could create a wrapper class for each purpose which only overrides equals() and hashcode(), but that seems very unsatisfying and inefficient.
I'm interested in how other people have dealt with this. Surprisingly, I have not been able to Google up much on this subject.
Two avenues of attack seem plausible. One, as you mention, is to
use a helper class to designate the chosen "identity" attributes. I
think I'd prefer to make it an inner class rather than a wrapper class,
but maybe that just means I'm still too hung up on your dogs and breeds.
The other approach is to implement your own BreedSet that uses
breedEquals() and breedHashCode() instead of the usual methods (and,
of course, documents that fact in large red letters). But this feels
an awful lot like the first step down a slippery slope, one that may
find you implementing umpty-leven specialized variations of Set and
Map and regretting the original choice ...
I'd be inclined to go with the inner class, or perhaps with a
wrapper if there's a reason you can't modify Dog. YMMV.
--
Eric Sosman
esosman@ieee-dot-org.invalid