Java Set can not distinguish different objects - java

I have one class Human, which contains two fields, age(int), and name(String). With eclipse, I override the hashCode() and equals() method with these two fields. I also create a Comparator based on the age field.
Now, I create a TreeSet object with the Comparator of age, and also two instances (with different fields values) of Human class. Then I add these two objects into the set, however, there are always only one object in the set.
For understanding the problem, I print out the hash value of these two objects, and find them are different. Then, I test their equals() method, it does output false when I compare two instances with different field values. So now, I cannot figure out why the TreeSet can't handle (differentiate) the problem. Can anyone give me some help ? Thanks a lot !

TreeSet doesn't use hashCode() and equals() at all. It uses the comparator you pass as argument (or the compareTo() method of the objects if they are Comparable and you don't provide a comparator). Two objects are considered equal by a TreeSet if compare() (orcompareTo()) returns 0 when comparing these two objects.
So if your comparator only compares the age of humans, all the humans with the same age will be considered equal. If you want humans to be considered equal when they have the same age and name, then the comparator should compare by age, and then compare by name if the age comparison returns 0.

From the documentation
This is so because the Set interface is defined in terms of the equals
operation, but a TreeSet instance performs all element comparisons
using its compareTo (or compare) method, so two elements 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.

Related

Java Object Ordering for Sorted Collections

When I'm looking at the Java Object Ordering tutorial, the last section 'Comparators' of the article confused me a little bit.
By defining a class Employee which itself is comparable by employee's name, the tutorial doesn't show if this class has overridden the equals method. Then it uses a customized Comparator in which the employees are sorted by the seniority to sort a list of employees and which I could understand.
Then the tutorial explains why this won't work for a sorted collection such as TreeSet (a SortedSet), and the reason is:
it generates an ordering that is not compatible with equals. This means that this Comparator equates objects that the equals method does not. In particular, any two employees who were hired on the same date will compare as equal. When you're sorting a List, this doesn't matter; but when you're using the Comparator to order a sorted collection, it's fatal. If you use this Comparator to insert multiple employees hired on the same date into a TreeSet, only the first one will be added to the set; the second will be seen as a duplicate element and will be ignored.
Now I'm confused, since I know List allows duplicate elements while Set doesn't based on equals method. So I wonder when the tutorial says the ordering generated by the Comparator is not compatible with equals, what does it mean? And it also says 'If you use this Comparator to insert multiple employees hired on the same date into a TreeSet, only the first one will be added to the set; the second will be seen as a duplicate element and will be ignored.' I don't understand how using a Comparator will affect the use of original equals method. I think my question is how the TreeSet will be produced and sorted in this case and when the compare and equals methods are used.
So I wonder when the tutorial says the ordering generated by the Comparator is not compatible with equals, what does it mean?
In this example, the Comparator compares two Employee objects based on their seniority alone. This comparison does not in any way use equals or hashCode. Keeping that in mind, when we pass this Comparator to a TreeSet, the set will consider any result of 0 from the Comparator as equality. Therefore, if any Employees share starting dates, only one will be added because the set thinks they are equal.
Finally:
I think my question is how the TreeSet will be produced and sorted in this case and when the compare and equals methods are used.
For the TreeSet, if a Comparator is given, it uses the compare method to determine equality and ordering of objects. If no Comparator is given, then the set uses the compareTo method of the objects being sorted (they must implement Comparable).
The reason why the Java specification claims that the compare/compareTo method being used must be in line with equals is because the Set specification makes use of equals, even though this specific type of Set, the TreeSet, uses comparisons instead.
If you ever receive a Set from some method implementation, you can expect that there are no duplicates of the objects in that Set as defined by the equals method. Because TreeSet doesn't use this method, however, developers must be careful to ensure that the comparison method results in the same equality as equals does.
A TreeSet uses only the Comparator to determine if two elements are "equal":
https://docs.oracle.com/javase/7/docs/api/java/util/TreeSet.html
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 element comparisons using its compareTo (or compare) method, so two elements 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.
This means that the Comparator should return 0 if and only if the equals returns true, to get a consistent behaviour between TreeSet and other sets, like a HashSet. The HashSet indeed uses equals and the hash code to determine if two elements are "equal".

Is it necessary to override equals and hashCode methods in a class if I use the objects of class to insert in a TreeSet only? [duplicate]

I have a quick question about TreeSet collections and hashCode methods. I have a TreeSet and I'm adding objects to it, before I add an object, I check to see if it exists in the TreeSet using the contains method.
I have 2 distinct objects, each of which produce a distinct hashCode using my implementation of the hashCode method, example below:
public int hashCode()
{
int hash = 7;
hash = hash * 31 + anAttribute.hashCode();
hash = hash * 31 + anotherAttribute.hashCode();
hash = hash * 31 + yetAnotherAttribute.hashCode();
return hash;
}
The hashCodes for a particular run are: 76126352 and 76126353 (the objects only differ by one digit in one attribute).
The contains method is returning true for these objects, even though the hashCodes are different. Any ideas why? This is really confusing and help would really be appreciated.
TreeSet does not use hashCode at all. It uses either compareTo or the Comparator you passed to the constructor. This is used by methods like contains to find objects in the set.
So the answer to your question is that your compareTo method or your Comparator are defined so that the two objects in question are considered equal.
From the javadocs:
a TreeSet instance performs all
element comparisons using its
compareTo (or compare) method, so two
elements that are deemed equal by this
method are, from the standpoint of the
set, equal.
From Java Doc:
If two objects are equal according to the equals(Object) method,
then calling the hashCode method on each of the two objects must
produce the same integer result.
Means: the objects you use for hashing are not equal.
You need to read Joshua Bloch's "Effective Java" chapter 3. It explains the equals contract and how to properly override equals, hashCode, and compareTo.
You don't need to checked if it is contained, because the insert() basically does the same operation (i.e. searching the proper position) on its way to the insertion point. If the object can't be inserted (i.e., the object is already contained), insert returns false.

Equals with multiple values Java

I've made a Java class in which I simulate a Polynome (it contains an ArrayList<Pairs>, each pair has its own coefficient and exponent). But now I like to compare two Polynome's with the equals() method (with an Object).
I know I can't say that this == that because this will only compare the Polynome references. SO now I wondered if there's an easy way to compare both Polynome's values, or do I just have to check the first Pair of the first Polynome to the first Pair of the second Polynome etc.?
You have to override the equals() method of your Pair class to return true if and only if both the coefficient and the exponent are equal.
You should also override the hashCode() method if you override equals(). Although strictly speaking this isn't mandatory while you're using ArrayLists, it is good practice to always override equals() and hashCode() together.
Also note that because you're using a List, where the order of elements matters, x3+2x-1 won't be equal to 2x-1+x3, which is probably not what you want to see. You should store your Pair objects in a Set instead, as their equals() doesn't rely on the order in which you added the elements to them.
You have to override equals and check each and every Monomial in the Polynomial collection.
Hint: I'd have two classes - Monomial, with its own equals and hashCode, and a separate Polynomial, which would have a Set of Monomials and its own equals and hashCode.

How to achieve always compareTo method should be consistent with the equals method in TreeMap or TreeSet? [duplicate]

If I implement java.lang.Comparable for a class, do I still have to override the equals() method? Or will the Comparable work for equals as well?
If the answer is no, then what if some discrepancy arises? Let's say the way I term two objects as equal within the equals() method is different from the way I term two objects of the same class as equal within the compareTo() of the Comparable.
Moreover, if I implement Comparable, do I also have to override equals()?
While it is recommended (and pretty sensible) that having a.compareTo(b) == 0 imply that a.equals(b) (and visa versa), it is not required. Comparable is intended to be used when performing an ordering on a series of objects, whereas equals() just tests for straight equality.
This link has some good information on implementing compareTo properly.
From Javadoc of java.lang.Comparable:
It is strongly recommended (though not required) that natural orderings be consistent with equals.
While it is recommended, it is not required that .equals() and .compareTo() have the same behaviour.
Just look at the BigDecimal Docs for equals() method:
Unlike compareTo, this method considers two BigDecimal objects equal
only if they are equal in value and scale (thus 2.0 is not equal to
2.00 when compared by this method).
BigDecimal is a core class of java that has different behaviour for equals() and compareTo() and serves as a good example of the difference between 2 objects that are comparable vs truly equal.
Let's say the way I term two objects as equal within the equals()
method is different from the way I term two objects of the same class
as equal within the toCompare() of the Comparable?
If you do this, and you put those objects into a sorted set, the set will misbehave. From the docs on SortedSet:
Note that the ordering maintained by a sorted set (whether or not an
explicit comparator is provided) must be consistent with equals if the
sorted set is to correctly implement the Set interface.
For example, a TreeSet may (erroneously) contain two objects where
a.compareTo(b) != 0
even though
a.equals(b) == true

Java's Comparator Contract

It says in the contract for the Comparator interface, that it must be consistent with equals.
Does this mean that Comparator = zero if equalsTo = true , or does it mean that Comparator = zero if and only if equalsTo = true?
I seem to remember that it is the second one, but I have come across lots of comparators which sort by non-unique sub properties.
For example, I might have objects which have a sub-property date, and I want to sort my list of objects by the date of submission. However, you can have several objects with the same date? What are the consequences of this? Surely there is a best practice solution to this problem already? How can I sort a collection by a property which is not guaranteed to be unique without violating the comparator contract? What are the consequences for this type of violation? Are they manageable?
It's not at all true that Comparator must be consistent with equals.
The docs merely warn for this situation:
Caution should be exercised when using a comparator capable of imposing an ordering inconsistent with equals to order a sorted set (or sorted map) (http://docs.oracle.com/javase/7/docs/api/java/util/Comparator.html)
If you have one ordering based on date, and another ordering based on date+time, you should simply implement multiple comparators.
Perhaps you are confusing Comparator with Comparable? For Comparable the docs strongly advice against this situation:
It is strongly recommended (though not required) that natural orderings be consistent with equals. (http://docs.oracle.com/javase/7/docs/api/java/lang/Comparable.html)
This difference makes sense if you realize that an object can only have 1 implementation of Comparable, but multiple of Comparator. The whole idea of Comparator is to have multiple ways of comparing the same class.
edit you could have mulitple Comparators and as popovitsj stated they don't necessarily have to be consistent with equals
(although I presume most of the time you have Comparator.compare(obj1, obj2) == 0 <=> obj1.equals(obj2) == true)
If you want to have specific sort results when sorting by non-unique field, you need to customize your Comparator to account for these,
for example, while implementing compare() you encounter that obj1.date == obj2.date, then you should compare other important fields (name, age, etc) to rank obj1 vs obj2 accordingly and return corresponding value.
Hope that helps.
As you suspect, in order for compareTo() to be consistent with equals(), compareTo() must always return 0 when equals() returns true. Similarly, if equals returns false, then compareTo must not return 0.
However, as popovitsj has pointed out in his answer, consistency with equals() is not a requirement. As such, the above only applies when you are attempting to make the two methods consistent.
It says in the contract for the Comparator interface, that it must be consistent with equals.
That's not entirely correct; see #popovitjs' answer.
Does this mean that Comparator = zero if equalsTo = true , or does it mean that Comparator = zero if and only if equalsTo = true?
It means the latter. However, it is not actually a hard requirement for Comparator objects.
I seem to remember that it is the second one, but I have come across lots of comparators which sort by non-unique sub properties.
Well that's reasonable, given that it is not actually a hard requirement. In fact, a Comparator that is inconsistent with equals(Object) is just fine if you are going to use it with Arrays.sort(...). The problems only arise with TreeSet and TreeMap.
For example, suppose that you have a Comparator<E> C that says e1 and e2 are not equal, but e1.equals(e2) returns true. Now suppose that you create a TreeSet<E> instance using the comparator, and then add e1 and e2 to that set. The set's tree is organized based on the comparator, and therefore e1 and e2 will slot into different places in the search tree, and will both be elements of the set. But that violates the primary invariant of a Set ... which is based on the equals method.
As the javadoc for TreeSet says:
"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 element comparisons using its compareTo (or compare) method, so two elements 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."
And this answers the last part of your question.
What are the consequences for this type of violation?
If you use an inconsistent Comparator in a TreeSet or TreeMap, the collection will not obey the Set or Map contract.
a and b may not be equal. But if comparator is zero when comparing a and b it should be zero when comparing b and a.
In this answer say:
Typically, if 2 objects are equal from an equals perspective but not from a compareTo perspective, you can store both objects as keys in a TreeMap. This can lead to un-intuitive behaviour. It can also be done on purpose in specific situations.
But this is for specific situations in general nothing stops you from having an inconsistant behaviour where equals and compareTo dont behave consistently.
One example, this morning someone asked: Move specific items to the end of a list
Most of the answers have comparators that returns 0 for elements that are not equal.

Categories