Java: external class for determining equivalence? - java

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?)

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.

Related

when to implement comparable and when to implement equals in Java

In Java, when should I implement Comparable<Something> versus implementing the equals method? I understand every time I implement equals I also have to implement hash code.
EDIT
Based on answers I am getting below:
Is it safe to say that if I implement Comparable then I don't need to implement equals and hashCode? As in: whatever I can accomplish with equal is already included in compareTo? For an example, I want to be able to compare two BSTs for equality. Implementing a hashCode for that seems daunting; so would comparable be sufficient?
If you only ever need to compare them for equality (or put them in a HashMap or HashSet which is effectively the same) you only need to implement equals and hashcode.
If your objects have an implicit order and you indend to sort them (or put them in a TreeMap or TreeSet which is effectively sorting) then you must implement Comparable or provide a Comparator.
Comparable is typically used for ordering items (like sorting) and equals is used for checking if two items are equal. You could use comparable to check for equality but it doesn't have to be. From the docs, "It is strongly recommended (though not required) that natural orderings be consistent with equals"
equals (and hashCode) are used for equality tests. The Comparable interface can be used for equality checks too, but it is in practice used for sorting elements or comparing their order.
That is, equals deals only with equality (similar to == and !=), while compareTo allows you to check many different types of inequality (similar to <, <=, ==, >=, > and !=). Therefore, if your class has a partial or total ordering of some kind, you may want to implement Comparable.
The relationship between compareTo and equals is briefly mentioned in the javadocs for Comparable:
It is strongly recommended, but not strictly required that (x.compareTo(y)==0) == (x.equals(y)). Generally speaking, any class that implements the Comparable interface and violates this condition should clearly indicate this fact. The recommended language is "Note: this class has a natural ordering that is inconsistent with equals."
Comparable is used by TreeSet to compare and sort anything you insert into it, and it is used by List#sort(null) to sort a list. It will be used in other places too, but those are the first that come to mind.
You should consider when you will use the object.
If for example in TreeMap and TreeSet, then you'll need Comparable. Also any case, when you will have to sort elements(especially sorted collections), implementing Comparable is mandatory. Overriding equals and hashcode will be needed in a very large amount of cases, most of them.
As per the javadocs:
This interface imposes a total ordering on the objects of each class that implements it. This ordering is referred to as the class's natural ordering, and the class's compareTo method is referred to as its natural comparison method.
Lists (and arrays) of objects that implement this interface can be sorted automatically by Collections.sort (and Arrays.sort). Objects that implement this interface can be used as keys in a sorted map or as elements in a sorted set, without the need to specify a comparator.
The natural ordering for a class C is said to be consistent with equals if and only if e1.compareTo(e2) == 0 has the same boolean value as e1.equals(e2) for every e1 and e2 of class C. Note that null is not an instance of any class, and e.compareTo(null) should throw a NullPointerException even though e.equals(null) returns false
In short, equals() and hashcode() are for comparing equality whereas Comparable is for sorting
If you update the equals method, you should always update hashCode or you will be setting a boobytrap for yourself or others when they try to use it in a HashMap or HasSet or similar collections (very commonly used).
Comparable is required only when the interfaces you are working with require it. You can implement it for use with sorting and sorted collections but it's not actually required in most cases. An alternate approach is to pass in a Comparator to the sort or collection constructor. For example the String class has a case insensitive Comparator that is very useful and eliminates the need to create your own String class (i.e. String cannot be extended.)

Unordered pair in Java

I need a Java generic class to represent unordered pairs of any type. Meanwhile I see two solutions:
HashSet to store the pair elements
a Pair class with overriden hashCode and equals (to make Pair(a, b) and Pair(b, a) equal).
Does it make sense? What would you suggest?
In your place I would roll out my own class. As long as you are interested in sets of only two objects, using HashMap, HashSet (which, incidentally, uses a HashMap internally anyway) or any other class designed for sets of arbitrary cardinality is a waste of resources and adds unneeded complexity.
Just create your own class with proper equals() and hashCode() implementations. Having a contains() operation, or even implementing parts of the Set interface, might also make sense.
One important note: make sure you document your class extensively - at least specify whether equals() performs an identity or an equality comparison for the contained objects, and what is the meaning of a null contained reference...

Why there is no Hashable interface in Java

Object in Java has hashCode method, however, it is being used only in associative containers like HashSet or HashMap. Why was it designed like that? Hashable interface having hashCode method looks as much more elegant solution.
The major argument seems to me that there is a well-defined default hashCode that can be calculated for any Java object, along with an equally well-defined equals. There is simply no good reason to withhold this function from all objects, and of course there are plenty reasons not to withhold it. So it's a no-brainer in my book.
This question is claimed as a duplicate from another which asks why there's no interface which behaves like Comparator (as distinct from Comparable) but for hashing. .NET includes such an interface, called IEqualityComparer, and it would seem like Java could as well. As it is, if someone wants to e.g. have a Java collection which e.g. maps strings to other objects in case-insensitive fashion (perhaps the most common use of IEqualityComparer) one must wrap the strings in objects whose hashCode and equals methods act on a case-insensitive basis.
I suspect the big issue is that while an "equalityComparer" interface could be convenient, in many cases efficiently testing an equivalence relation would require caching information. For example, while a case-insensitive string-hashing function could make an uppercase-only copy of the passed-in string and call hashCode on that, it would be difficult to avoid having every request for the hashcode of a particular string repeat the conversion to uppercase and the hashing of that uppercase value. By contrast, a "case-insensitive string" object could include fields for an uppercase-only copy of the string, which would then only have to be generated once for the instance.
An EqualityComparer could achieve reasonable performance if it included something like a WeakHashMap<string,string> to convert raw strings to uppercase-only strings, but such a design would either require different threads to use different EqualityComparer instances despite the lack of externally visible state, or else require performance-robbing locking and synchronization code even in single-threaded scenarios.
Incidentally, a second issue that arises with comparator-style interfaces is that a collection type which uses an externally-supplied comparator (whether it compares for rank or equality) is that the comparator itself becomes part of the state of the class which uses it. If hash tables use different EqualityComparer instances, there may be no way to know that they can safely be considered equivalent, even if the two comparators would behave identically in all circumstances.

TreeSet and equals function

There is a Java bean object which has implemented equals function based on certain criteria (Criteria A). I have a requirement to identify unique objects based on another criteria (Criteria B). Since the equals function uses criteria A, I can not use HashSet. So I thought of using TreeSet with my custom Comparator which is based on criteria B. My question is, Is it allowed to do like this? Any issues with this approach?
Thank you.
Here is some guide from Oracle Java:
Note that the ordering maintained by a
set (whether or not an explicit
comparator is provided) must be
consistent with equals if it is to
correctly implement the Set interface.
(See Comparable or Comparator for a
precise definition of consistent with
equals.) This is so because the Set
interface is defined in terms of the
equals operation, but a TreeSet
instance performs all key comparisons
using its compareTo (or compare)
method, so two keys that are deemed
equal by this method are, from the
standpoint of the set, equal. The
behavior of a set is well-defined even
if its ordering is inconsistent with
equals; it just fails to obey the
general contract of the Set interface.
I think in terms of technical, no, you don't have any problems. But, in terms of coding, readability and maintainability, you have to be careful, because other people can misuse or misunderstand what you are doing
If you perform search often and add elements rarely, consider keeping them in a List sorted by Criteria B and using Collections.binarySearch.
You can wrap them:
class BeanWrapper {
...
public boolean equals(Object other) {
return myBean.critB.equals(((Bean)other).critB);
}
}
And put them in the set like that.

Should compareTo and equals be consistent if the Object is mutable and will not be used in sorted Set, etc

The Java documentation for Comparable recommends that compareTo be consistent with equals (because of the behavior in sorted sets or sorted maps).
I have a Player object which is mutable. Player will not be used in any kind of sorted set or sorted map.
I want to have an array of Players (Player[]) and assign each player a random order of play (a member variable m_nPlayOrder), then sort them in that array based on their m_nPlayOrder member variable.
I will implement the Comparable interface and the compareTo function to achieve this.
My question is, is it ok in this case if Player has a compareTo that is NOT consistant with equals? It will not be consistant with equals unless I also override equals (and hashCode) and have it return true if the Players m_nPlayerOrder are equal. I do not want to do that.
UPDATE:
Thanks everyone for the replys! I'm going with implementing the Comparator instead of Comparable.
You could implement a java.util.Comparator (a stand-alone object that "knows" your Player class and compares accordingly) and use that instead of implementing Comparable in the class.
Comparator has the advantage in that you can implement any number of them for different types of sorts, rather than the single Comparable.
It is not a good practice to implement equals and compareTo that are inconsistent.
Player will not be used in any kind of sorted set or sorted map.
The problem is that someone else trying to maintain your code at some time in the future may not realize that you have implemented inconsistent methods, and may try to put a Player into a sorted set/map or some other data structure that expects consistent methods.
There is a good alternative available to you in the form of standalone Comparator objects.
I do not think you have the problem that you think you have, but perhaps I'm missing something (wouldn't be the first time).
The requirement here is that 2 objects that are equal should also return 0 for compareTo(), meaning that they occupy the same rank for sorting purposes. There is a separate requirement that 2 equal objects should also have the same hashCode. But this isn't a concern for you, because you're overriding neither equals() nor hashCode(); the Java platform will satisfy this one for you.
Since your objects will be equal based on reference comparison (not custom, logical comparison), you won't have a problem unless your objects don't compare to themselves. In orther words, as long as a single object, when asked to compareTo() itself, would always return 0, you're fine.
The Comparable API says, "It is strongly recommended (though not required) that natural orderings be consistent with equals." If you don't, be certain to document the limitations you noted. Alternatively, it may be easy to implement Comparable fully, as suggested in this answer.

Categories

Resources