8

Java has a Comparator<T> for providing comparison of objects external to the class itself, to allow for multiple/alternate methods of doing ordered comparisons.

But the only standard way of doing unordered comparisons is to override equals() within a class.

What should I do when I want to provide multiple/alternate unordered comparisons external to a class? (Obvious use case is partitioning a collection into equivalence classes based on particular properties.)

Assuming the end use is for unordered checking (e.g. not for sorting or indexing), is it ever OK to implement Comparator<T> that just checks for equality, returning 0 if two objects are equal, and a value != 0 when two objects are unequal? (note: the only reason I don't jump on this solution, is that technically it can break the contract for Comparator by not providing a relation that satisfies transitivity and symmetry.)

It seems like there should have been an EqualsComparator<T> standard class or something.

(Does Guava handle anything like this?)

Jason S
  • 184,598
  • 164
  • 608
  • 970

1 Answers1

14

Yes, Guava has the Equivalence interface and the Equivalences class (Removed in Guava release 14.0).

(And yes, it's something which is very useful and sadly lacking in Java. We really should have options around this for HashMap, HashSet etc...)

While Comparator<T> may be okay in some situations, it doesn't provide the hashCode method which would be important for hash-based collections.

Iulian Popescu
  • 2,595
  • 4
  • 23
  • 31
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • hmmm... both are tagged @Beta though. – Jason S Aug 31 '11 at 15:14
  • @Jason: So use with care :) (Ultimately `Equivalence` is an interface, so it's not like you're relying on an *implementation* which might go away.) – Jon Skeet Aug 31 '11 at 15:15
  • 5
    I can promise it won't go away, but it is in flux. For example for the impending version 10.0 we've changed it to an abstract class and moved most of Equivalences directly into it (and will probably finish the job at some point). – Kevin Bourrillion Aug 31 '11 at 18:37
  • As for _collections_ built on arbitrary equivalence, though, I am emphatically not a believer. Java has too many years of baked-in assumptions (and explicit `Collection` contracts) requiring `equals`. Instead, you can use `yourEquivalence.wrap(yourObject)` and store the wrappers in a collection; _clunky_, but clear. – Kevin Bourrillion Aug 31 '11 at 18:39
  • @Kevin yeah, it's a pity you guys think that way :-) http://code.google.com/p/guava-libraries/issues/detail?id=576 – Sean Patrick Floyd Sep 02 '11 at 08:12
  • You would _think_ it's a pity, but no; we had the advantage of getting to see what happened when we tried having that functionality in `MapMaker`. It was used to make code harder to understand for no real benefit in every case. We improved all that code by having it stop using our misfeature, then we removed the misfeature. – Kevin Bourrillion Sep 06 '11 at 21:46
  • @Kevin: On the other hand, it's prevalent in .NET everywhere that uses equivalent, and I haven't seen it make code harder to understand - and it *does* have benefits in terms of things like case-insensitive sets. (There's *one* case where it niggles me, but that's a rare one we can talk about over a coffee some time.) Overall I think .NET is better for having this pattern - although I do see the point that having it in *some* places in Java would possibly be worse than nowhere. – Jon Skeet Sep 06 '11 at 21:49
  • Again, "Java has too many years of baked-in assumptions (and explicit Collection contracts." .NET doesn't. – Kevin Bourrillion Sep 08 '11 at 13:28
  • @Kevin: Yup, agreed. It wasn't clear to me whether you were *only* talking about the history of Java, or whether you didn't believe in collections built on arbitrary equivalence *in principle*. – Jon Skeet Sep 08 '11 at 13:31