Why isn't there a comparator for "equals()"?

M

Michael Strorm

Hi,
I have a class which provides several definitions of equality.
equals() uses a particular definition depending on a value set via the
(static) method Classname.setEqualityMethod(VALUE).

It doesn't take a genius to see that this is very error-prone (not
thread-safe, hard to keep track of the current equalityMethod set). I
suspected it would come back and bite me in a horrible way, and to cut
a long story short, it did.

Well, it seems I can define comparators for collections, but not
equality. Is there a trivial solution to this, and why was it done
this way?

I've heard that it should be possible to work round this by
implementing your own collection, but this sounds like overkill to me.
My code is bloated and badly-designed enough as it is.

Any help appreciated, thank you.

- MS
 
T

Tor Iver Wilhelmsen

Well, it seems I can define comparators for collections, but not
equality. Is there a trivial solution to this, and why was it done
this way?

In a way equals() is a specialization of compare(): Two objects are
equal if compare()/compareTo() returns 0, and not equal for any other
result.

Isn't that sufficient?
 
T

Tony Morris

In a way equals() is a specialization of compare(): Two objects are
equal if compare()/compareTo() returns 0, and not equal for any other
result.

This is not mandated behaviour for a Comparable/Object.equals.
"It is strongly recommended (though not required) that natural orderings be
consistent with equals."

--
Tony Morris

JTiger Unit Test Framework for J2SE 1.5
http://www.jtiger.org/
Java Q&A (FAQ, Trivia)
http://qa.jtiger.org/
http://xdweb.net/~dibblego/
 
M

Michael Strorm

Tony Morris said:
This is not mandated behaviour for a Comparable/Object.equals.
"It is strongly recommended (though not required) that natural orderings be
consistent with equals."

More importantly, if I am doing something that requires an equality
check, the comparator is irrelevant, as equals() is the method that is
called when searching and removing objects from a collection.

So, although Tor was right in a sense to say that equality is a
specialisation of comparison, the way Java works keeps them separate,
permitting them to disagree.

In short, I can't *do* what I want to do by designing different
comparators, because equals() doesn't use them. Of course, I could get
equals to call compare(), but that would still require a comparator to
be part of the class, as there is no way(??) of referencing an
external comparator.

Then I'd have to set the comparator inside every object I wanted to
compare with the new method; that's even more hideous than what I'm
doing just now! :-6

- MS
 
E

Eric Sosman

Michael said:
Hi,
I have a class which provides several definitions of equality.
equals() uses a particular definition depending on a value set via the
(static) method Classname.setEqualityMethod(VALUE).

It doesn't take a genius to see that this is very error-prone (not
thread-safe, hard to keep track of the current equalityMethod set). I
suspected it would come back and bite me in a horrible way, and to cut
a long story short, it did.

Well, it seems I can define comparators for collections, but not
equality. Is there a trivial solution to this, and why was it done
this way?

Are you really trying to define a different equals()
method for a Collection? It sounds to me like you're
trying to juggle the equals() method of the class whose
instances the Collection contains. You can do this (it
sounds like you already have), but it seems a bad idea.

In particular, note that you can invalidate an existing
Collection of your objects by changing the behavior of equals()
after objects have already been inserted:

Thing thing = new Thing(42);
List list = new ArrayList();
list.add(thing);
Thing.setEqualityMethod(Thing.SOMETHING_ELSE);
if (list.contains(thing)) ...

This might very well fail to find the `thing' that you've
already placed in the List.
I've heard that it should be possible to work round this by
implementing your own collection, but this sounds like overkill to me.
My code is bloated and badly-designed enough as it is.

Perhaps it's time to take a wider view of the overall
problem, the one you're currently trying to solve by mutating
equals(). What situation are you trying to model? Maybe
there's a less fragile way.
 
V

Virgil Green

Michael said:
Hi,
I have a class which provides several definitions of equality.
equals() uses a particular definition depending on a value set via the
(static) method Classname.setEqualityMethod(VALUE).

It doesn't take a genius to see that this is very error-prone (not
thread-safe, hard to keep track of the current equalityMethod set). I
suspected it would come back and bite me in a horrible way, and to cut
a long story short, it did.

Well, it seems I can define comparators for collections, but not
equality. Is there a trivial solution to this, and why was it done
this way?

I've heard that it should be possible to work round this by
implementing your own collection, but this sounds like overkill to me.
My code is bloated and badly-designed enough as it is.

Any help appreciated, thank you.

- MS

What was the problem with your setEqualityMethod() approach? Did a method
get changed?

Since we don't know what the actual goal is, let me make this suggestion.
Perhaps you could make the EqualityMethod an immutable property with lazy
initialization. Once set, the property becomes immutable. You can observe it
via a getEqualityMethod() method, but any call to setEqualityMethod() after
initially set would throw an exception.

Or maybe you need to be able to change EqualityMethod values over time for a
given object.

We can help a lot more if we know what the actual problem is.
 
P

Patricia Shanahan

Michael said:
Hi, I have a class which provides several definitions of
equality. equals() uses a particular definition depending
on a value set via the (static) method
Classname.setEqualityMethod(VALUE).

Does each collection use a fixed model of equality? When
adding objects to a collection, do you know which meaning
applies?

Patricia
 
H

hiwa

Eric said:
Perhaps it's time to take a wider view of the overall
problem, the one you're currently trying to solve by mutating
equals().
Bingo! Bad design often bears queer requirment(s).
 
T

Thomas G. Marshall

Michael Strorm coughed up:
Hi,
I have a class which provides several definitions of equality.
equals() uses a particular definition depending on a value set via the
(static) method Classname.setEqualityMethod(VALUE).

It doesn't take a genius to see that this is very error-prone (not
thread-safe, hard to keep track of the current equalityMethod set). I
suspected it would come back and bite me in a horrible way, and to cut
a long story short, it did.

Well if it didn't I would have been truly amazed.

It sounds as if what you need an equals() that takes the method of equality
with it (?)

thing.equals({equality type}, {object to compare})

Or at the very least have the "setEqualityMethod()" be /only/ as a parameter
to the constructor.

I'm not sure if this exceeds your desire to code as little as possible.

....[rip]...


--
Having a dog that is a purebred does not qualify it for breeding. Dogs
need to have several generations of clearances for various illnesses
before being bred. If you are breeding dogs without taking care as to
the genetic quality of the dog (again, being purebred is *not* enough),
you are what is known as a "backyard breeder" and are part of the
problem. Most of the congenital problems of present day dogs are
traceable directly to backyard breeding. Spay or neuter your pet
responsibly, and don't just think that you're somehow the exception and
can breed a dog without taking the care described.
 
A

Adam P. Jenkins

hiwa said:
Bingo! Bad design often bears queer requirment(s).

True in general, but in this case I think the original poster (OP) has a
point (with the caveat that he didn't specify specifically what he wants
to do.) Java's java.util.TreeSet and java.util.TreeMap in fact have
exactly the feature that the OP is asking for -- they have constructors
which accept a Comparator argument, which will be used instead of the
contained elements' Comparable interface. I myself have on occasion
wished that Sun had been consistent and made the other Collection
classes have the same feature. In C++'s standard library every
collection class has an overloaded constructor which takes a comparator
argument, and I think I remember that the same is true for the ML and
OCaml standard library classes.

Adam
 
D

Dale King

T

Thomas G. Marshall

Adam P. Jenkins coughed up:
True in general, but in this case I think the original poster (OP)
has a point (with the caveat that he didn't specify specifically what
he wants to do.) Java's java.util.TreeSet and java.util.TreeMap in
fact have exactly the feature that the OP is asking for -- they have
constructors

Whoa, a constructor is a far safer way to do this. The comparator used for
the object is established only once, and is unchangeable. The OP's current
notion was to have this as a restriction free mutator, which is a disaster
waiting to happen.


which accept a Comparator argument, which will be used
instead of the contained elements' Comparable interface. I myself
have on occasion wished that Sun had been consistent and made the
other Collection classes have the same feature. In C++'s standard
library every collection class has an overloaded constructor which
takes a comparator argument, and I think I remember that the same is
true for the ML and OCaml standard library classes.

Adam



--
Unix users who vehemently argue that the "ln" command has its arguments
reversed do not understand much about the design of the utilities. "ln
arg1 arg2" sets the arguments in the same order as "mv arg1 arg2".
Existing file argument to non-existing argument. And in fact, mv
itself is implemented as a link followed by an unlink.
 
A

Adam P. Jenkins

Thomas said:
Adam P. Jenkins coughed up:


Whoa, a constructor is a far safer way to do this. The comparator used for
the object is established only once, and is unchangeable. The OP's current
notion was to have this as a restriction free mutator, which is a disaster
waiting to happen.

I didn't interpret the OP's post that way. I thought he was saying he
wished the Java Collections Framework provided a way to specify
different equality predicates for particular collection instances, but
since it didn't, he'd resorted to the "disaster waiting to happen"
method of making the objects in the collection have an equalityMethod
property, which changed the behavior of .equals(). He even admitted in
his original post that this was very error-prone, so I don't think he
was advocating his technique; rather he was asking for a better
alternative short of writing his own collection classes.

Unfortunately, I don't know of a good alternative in Java short of
writing one's own collection classes. I think being able to associate a
comparator or equality predicate with a collection instance is a very
obviously useful feature, and as I pointed out, several standard
libraries for other languages do in fact have this feature.

Adam
 
T

Thomas G. Marshall

Adam P. Jenkins coughed up:
I didn't interpret the OP's post that way. I thought he was saying he
wished the Java Collections Framework provided a way to specify
different equality predicates for particular collection instances, but
since it didn't, he'd resorted to the "disaster waiting to happen"
method of making the objects in the collection have an equalityMethod
property, which changed the behavior of .equals().

Then you and I interpreted his post precisely the same way. Having that
equalityMethod property free to mutate at will is indeed that disaster.
Having the TreeSet and TreeMap technique of establishing this at
construction is far safer: it happens only at one point, and the rules can't
change out from under you.


He even admitted
in his original post that this was very error-prone, so I don't think
he was advocating his technique;

Of course he wasn't. I never said nor implied that he did.

rather he was asking for a better
alternative short of writing his own collection classes.

Unfortunately, I don't know of a good alternative in Java short of
writing one's own collection classes.

I don't know of one either, and you, I, and the OP certainly agree that his
mechanism is not going to work. I might suggest a simple wrapper to a
collection. Or better yet: a decorator pattern, that takes at construction
a collection of some sort, and provides wrapped methods to only the
collection contracted methods he needs. Equality would then follow some
internal state.

But he's still stuck with the following safe guards to implement: MT is
going to potentially reek havoc by changing comparator context mid stream.
And it's not clear that you can mutex entirely around the large chunks of
code that might need the comparator context intact. In other words, perhaps
 
M

Michael Strorm

Patricia Shanahan said:
Does each collection use a fixed model of equality? When
adding objects to a collection, do you know which meaning
applies?

I'd be quite happy to be able to assign a fixed-version of equality
(via equals()) to a Collection in the same way that I can assign a
comparator...
if it were possible.

Actually, that's what I'd really like.

- MS
 
M

Michael Strorm

Thomas G. Marshall said:
It sounds as if what you need an equals() that takes the method of equality
with it (?)

thing.equals({equality type}, {object to compare})

That'd be passable, if Collections would call it; which they won't
AFAIK.
Or at the very least have the "setEqualityMethod()" be /only/ as a parameter
to the constructor.

Good idea; thought of it, but it doesn't work because I want do do
different types of equality check on the same objects.

- MS
 
M

Michael Strorm

Adam P. Jenkins said:
I didn't interpret the OP's post that way. I thought he was saying he
wished the Java Collections Framework provided a way to specify
different equality predicates for particular collection instances

Bingo! Let me make clear that I do *not* want to change the definition
of equals() for a given collection; on the contrary, I'd be quite
happy to provide an equality-comparator object to the collection's
constructor, and leave it at that.

He even admitted in
his original post that this was very error-prone, so I don't think he
was advocating his technique

I was *definitely* not advocating this technique. On the contrary, it
was obvious almost as soon as I'd written it that way that the design
was horrid. :-6

- MS
 
M

Michael Strorm

Dale King said:
I am wondering if what he is really looking for is the concept of a
Predicate which you can see in the jakarta commons collections:

It allows you to specify different criteria for filtering a collection
among other things based on varying criteria.

Thanks; but I want to do searches within, and removal from, a
Collection, which basically require equality to be defined. I could
perhaps use Predicates in a roundabout manner to do that, but I don't
think it would be what they were intended for, and the code would
probably be quite tacky.

- MS
 
P

Phillip Lord

Michael> More importantly, if I am doing something that requires an
Michael> equality check, the comparator is irrelevant, as equals()
Michael> is the method that is called when searching and removing
Michael> objects from a collection.

Michael> So, although Tor was right in a sense to say that equality
Michael> is a specialisation of comparison, the way Java works keeps
Michael> them separate, permitting them to disagree.

Michael> In short, I can't *do* what I want to do by designing
Michael> different comparators, because equals() doesn't use
Michael> them. Of course, I could get equals to call compare(), but
Michael> that would still require a comparator to be part of the
Michael> class, as there is no way(??) of referencing an external
Michael> comparator.

Michael> Then I'd have to set the comparator inside every object I
Michael> wanted to compare with the new method; that's even more
Michael> hideous than what I'm doing just now! :-6


Can't you use a wrapper object? If you stick your object inside
another, then you can effectively change the behaviour of equal in
whatever way you want. If the wrapper has no state itself, then you
only need one, much like a comparitor.

A bit ugly, as you need to remember to wrap and unwrap the objects
that you are putting into the collection. Easier with generics to
remind you, but still a pain in the ass.

Phil
 
M

Michael Strorm

Phillip Lord said:
Can't you use a wrapper object?

Yep; that's the one I'm going for, if any. Thanks.
A bit ugly, as you need to remember to wrap and unwrap the objects
that you are putting into the collection. Easier with generics to
remind you, but still a pain in the ass.

Could always extend the desired Collection type to check the type
being added in the add() methods, then delegate to the super method, I
guess.

- MS
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top