When to include what? - java

I created a class Person (as the book says) to hold the name and last name of a person entered from the keyboard and then there is another class PhoneNumber which encapsulates the country code, area code and the number of a person as a String.
Person is intended to be used as the key in a Hashmap.
Class BookEntry encapsulates both Person and PhoneNumber. A lot of BookEntry objects make up a HashMap that represents a phonebook.
Person implements Comparable<Person> so it contains CompareTo(Person) method. Later the book adds equals(Object anotherPerson)method.
My question is, isn't the CompareTo method enough for comparing two keys? or is it that the internal mechanics of the HashMap<> requires me to include equals() method to compare two keys?
compareTo()
public int compareTo(Person person) {
int result = lastName.compareTo(person.lastName);
return result==0? firstName.compareTo(person.firstName):result;
}
equals()
public boolean equals(Object anotherPerson){
return compareTo((Person)person)==0;
}

Some data structures will use compareTo (for example a TreeMap) and some will use equals (for example a HashMap).
More importantly, it is strongly recommended that compareTo and equals be consistent, as explained in the Comparator javadoc:
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."
Another hint, found in TreeMap javadoc (emphasis mine):
Note that the ordering maintained by a tree map, like any sorted map, and whether or not an explicit comparator is provided, must be consistent with equals if this sorted map is to correctly implement the Map interface.
Finally, if you override equals you should also override hashcode to prevent unexpected behaviours when using hash-based structures.

compareTo() method is used in sorting,
This method's implementation will determine who is greater(lesser, same) between two person, also at what degree
while equals() & hashcode() will be used in Hash based data structure (HashMap) in your case
user-defined class as a key of HashMap
yes you need to implement hashcode() and equals() properly
Also See
overriding-equals-and-hashcode-in-java

HashMap uses equals() and not compareTo(), so you have to implement it.
TreeMap uses compareTo().

Related

Is it possible that TreeSet equals HashSet but not HashSet equals TreeSet

I had a interview today and the person taking my interview puzzled me with his statement asking if it possible that TreeSet equals HashSet but not HashSet equals TreeSet. I said "no" but according to him the answer is "yes".
How is it even possible?
Your interviewer is right, they do not hold equivalence relation for some specific cases. It is possible that TreeSet can be equal to HashSet and not vice-versa. Here is an example:
TreeSet<String> treeSet = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
HashSet<String> hashSet = new HashSet<>();
treeSet.addAll(List.of("A", "b"));
hashSet.addAll(List.of("A", "B"));
System.out.println(hashSet.equals(treeSet)); // false
System.out.println(treeSet.equals(hashSet)); // true
The reason for this is that a TreeSet uses comparator to determine if an element is duplicate while HashSet uses equals.
Quoting TreeSet:
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.
It’s not possible without violating the contract of either equals or Set. The definition of equals in Java requires symmetry, I.e. a.equals(b) must be the same as b.equals(a).
In fact, the very documentation of Set says
Returns true if the specified object is also a set, the two sets have the same size, and every member of the specified set is contained in this set (or equivalently, every member of this set is contained in the specified set). This definition ensures that the equals method works properly across different implementations of the set interface.
NO, this is impossible without violating general contract of the equals method of the Object class, which requires symmetry, i. e. x.equals(y) if and only if y.equals(x).
BUT, classes TreeSet and HashSet implement the equals contract of the Set interface differently. This contract requires, among other things, that every member of the specified set is contained in this set. To determine whether an element is in the set the contains method is called, which for TreeSet uses Comparator and for HashSet uses hashCode.
And finally:
YES, this is possible in some cases.
This is a quote from the book Java Generics and Collections:
In principle, all that a client should need to know is how to keep to
its side of the contract; if it fails to do that, all bets are off and
there should be no need to say exactly what the supplier will do.
So the answer is : Yes it can happen but only when you don't keep to your side of the contract with Java. Here you can say Java has violated the symmetric property of equality but if that happen be sure that you are the one who has broken the contract of some other interfaces first. Java has already documented this behaviour.
Generally you should read documentation of Comparator and Comparable interfaces to use them correctly in sorted collections.
This question is somehow answered in Effective Java Third Edition Item 14 on pages 66-68.
This is a quote from the book when defining contract for implementing Comparable interface(note that this is only part of the whole contract):
• It is strongly recommended, but not 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.”
It says It is strongly recommended, but not required, it means you are allowed to have classes for which
x.compareTo(y)==0 does not mean x.equal(y)==true.(But if it is implemented that way you can't use them as an element in sorted collections, this is exactly the case with BigDecimal)
The paragraph of the book describing this part of the contract of Comparable interface is worth mentioning:
It is a strong suggestion rather than a true requirement, simply
states that the equality test imposed by the compareTo method should
generally return the same results as the equals method. If this
provision is obeyed, the ordering imposed by the compareTo method is
said to be consistent with equals. If it’s violated, the ordering is
said to be inconsistent with equals. A class whose compareTo method
imposes an order that is inconsistent with equals will still work, but
sorted collections containing elements of the class may not obey the
general contract of the appropriate collec- tion interfaces
(Collection, Set, or Map). This is because the general contracts for
these interfaces are defined in terms of the equals method, but sorted
collec- tions use the equality test imposed by compareTo in place of
equals. It is not a catastrophe if this happens, but it’s something to
be aware of.
Actually we have some classes in Java itself that did not follow this recommendation. BigDecimal is one of them and this is mentioned in the book.
For example, consider the BigDecimal class, whose compareTo method is
inconsistent with equals. If you create an empty HashSet instance and
then add new BigDecimal("1.0") and new BigDecimal("1.00"), the set
will contain two elements because the two BigDecimal instances added
to the set are unequal when compared using the equals method. If,
however, you perform the same procedure using a TreeSet instead of a
HashSet, the set will contain only one element because the two
BigDecimal instances are equal when compared using the compareTo
method. (See the BigDecimal documentation for details.)
However this behaviour is documented in BigDecimal Documentation. Let's have a look at that part of the documentation:
Note: care should be exercised if BigDecimal objects are used as keys
in a SortedMap or elements in a SortedSet since BigDecimal's natural
ordering is inconsistent with equals. See Comparable, SortedMap or
SortedSet for more information.
So although you can write code like below you should not do it because the BigDecimal class has prohibited this usage:
Set<BigDecimal> treeSet = new TreeSet<>();
Set<BigDecimal> hashSet = new HashSet<>();
treeSet.add(new BigDecimal("1.00"));
treeSet.add(new BigDecimal("2.0"));
hashSet.add(new BigDecimal("1.00"));
hashSet.add(new BigDecimal("2.00"));
System.out.println(hashSet.equals(treeSet)); // false
System.out.println(treeSet.equals(hashSet)); // true
Note that Comparable will be used as natural ordering of the elements when you don't pass any comparator to TreeSet or TreeMap, the same thing can happen when you pass Comparator to those class constructor. This is mentioned in the Comparator documentation:
The ordering imposed by a comparator c on a set of elements S is said
to be consistent with equals if and only if c.compare(e1, e2)==0 has
the same boolean value as e1.equals(e2) for every e1 and e2 in S.
Caution should be exercised when using a comparator capable of
imposing an ordering inconsistent with equals to order a sorted set
(or sorted map). Suppose a sorted set (or sorted map) with an explicit
comparator c is used with elements (or keys) drawn from a set S. If
the ordering imposed by c on S is inconsistent with equals, the sorted
set (or sorted map) will behave "strangely." In particular the sorted
set (or sorted map) will violate the general contract for set (or
map), which is defined in terms of equals.
So considering this documention of Comparator, following example given by #Aniket Sahrawat is not supported to work:
TreeSet<String> treeSet = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
HashSet<String> hashSet = new HashSet<>();
treeSet.addAll(List.of("A", "b"));
hashSet.addAll(List.of("A", "B"));
System.out.println(hashSet.equals(treeSet)); // false
System.out.println(treeSet.equals(hashSet)); // true
In a nutshell the answer is: Yes it can happen but only when you break the documented contract of one of the aforementioned interfaces(SortedSet, Comparable, Comparator).
There already are good answers, but I would like to approach this from a bit more general perspective.
In the Mathematics, Logic, and correspondingly, in the Computer Science, "is equal to" is a Symmetric Binary Relation, which means, that if A is equal to B then B is equal to A.
So, if TreeSet X equals HashSet Y, then HashSet Y must equal to TreeSet X, and that must be true always.
If, however, symmetric property of the Equality is violated (i.e. Equality is not implemented correctly), then x.equals(y) might not mean y.equals(x).
The documentation of Object#equals method in Java, explicitly states, that:
The equals method implements an equivalence relation on non-null object references.
hence, it implements the symmetric property, and if it does not, then it violates the Equality, in general, and violates the Object#equals method, specifically in Java.

Saving our own objects as keys in TreeMap

I was just wondering if there is any consideration to have in account when saving our own objects in a TreeMap. Something similar when we save our own objects as keys in a hashmap that we need to override equals and hashcode method to be able to retrieve them later. In a treemap there is no hash, a black red algorith is used, but I don't know if there is something special to do.
If so, could you tell me if there is something to have in account?
Thanks
The javadoc says:
The map is sorted according to the natural ordering of its keys, or by a Comparator provided at map creation time
So you need to implement a natural ordering correctly, or implement a Comparator correctly.
It also says:
Note that the ordering maintained by a tree map, like any sorted map, and whether or not an explicit comparator is provided, must be consistent with equals if this sorted map is to correctly implement the Map interface. (See Comparable or Comparator for a precise definition of consistent with equals.) This is so because the Map interface is defined in terms of the equals operation, but a sorted map 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 sorted map, equal. The behavior of a sorted map is well-defined even if its ordering is inconsistent with equals; it just fails to obey the general contract of the Map interface.
So, if you want to obey the general contract of Map (and you should, generally), the compareTo() method must be consistent with equals(), which means that you need to correctly implement an equals() method, and transitively, a hashCode() method, and that you must make sure that a.equals(b) iff e.compareTo(b) == 0.
Most of the times, people screw up because they implement a compareTo/compare method that returns 0 for two objects, and still expect these two objects to be considered different by the map.
Apart from equals and compareTo transitivity there's one more thing that's terribly important.
Your keys, or at least the fields you're using for comparison, should be immutable.
And you can actually use anything as a key for a TreeMap as long as you provide custom Comparator in its constructor.

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".

Do I need a equals and Hashcode method if my class implements comparable in Java?

I found this comment on can StringBuffer objects be keys in TreeSet in Java?
"There are 2 identifying strategies used with Maps in Java (more-or-less).
Hashing: An input "Foo" is converted into a best-as-possible attempt to generate a number that uniquely accesses an index into an array. (Purists, please don't abuse me, I am intentionally simplifying). This index is where your value is stored. There is the likely possibility that "Foo" and "Bar" actually generate the same index value meaning they would both be mapped to the same array position. Obviously this can't work and so that's where the "equals()" method comes in; it is used to disambiguate
Comparison: By using a comparative method you don't need this extra disambiguation step because comparison NEVER produces this collision in the first place. The only key that "Foo" is equal to is "Foo". A really good idea though is if you can is to define "equals()" as compareTo() == 0; for consistency sake. Not a requirement."
my question is as follows:
if my class implements comparable, then does it mean I dont have to override equals and hashcode method for using my objects as keys in Hash collections. eg
class Person implements Comparable<Person> {
int id;
String name;
public Person(int id, String name) {
this.id=id;
this.name=name;
}
public int compareTo(Person other) {
return this.id-other.id;
}
}
Now, can I use my Person objects in Hashable collections?
The article you brough is talking on TreeSet. a tree set is a tree with each node has a place defined by it's value in compare to the other values already in the tree.
a hashTable stores key/value pairs in a hash table. When using a Hashtable, you specify an object that is used as a key, and the value that you want linked to that key. The key is then hashed, and the resulting hash code is used as the index at which the value is stored within the table.
the difference between Hashable and TreeSet is that treeset don't need hashCode, it just need to know if you need the take the item left or right in the tree. for that you can use Compare and nothing more.
in hashTable a compare will suffice, because it's build differently, each object get to his cell by hashing it, not by comparing it to the items already in the collection.
so the answer is no, you can' use Person in hashtable just with compareTo. u must override hashCode() and equals() for that
i also suggest you read this article on hashtables
HashTable does use equals and hashCode. Every class has those methods. If you don't implement them, you inherit them.
Whether you need to implement them depends on whether the inherited version is suitable for your purposes. In particular, since Person has no specified superclass, it inherits the Object methods. That means a Person object is equal only to itself.
Do you need two distinct Person objects to be treated as being equal as HashTable keys?
if my class implements comparable, then does it mean I dont have to override equals and hashcode method for using my objects as keys in Hash collections. eg
No, you still need to implement equals() and hashCode(). The methods perform very different functions and cannot be replaced by compareTo().
equals() returns a boolean based on equality of the object. This is usually identity equality and not field equality. This can be very different from the fields used to compare an object in compareTo(...) although if it makes sense for the entity, the equals() method can be:
#Overrides
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != getClass()) {
return false;
} else {
return compareTo((Person)obj) == 0;
}
}
hashCode() returns an integer value for the instance which is used in hash tables to calculate the bucket it should be placed in. There is no equivalent way to get this value out of compareTo(...).
TreeSet needs Comparable, to add values to right or left of tree. HashMap needs equals() and Hashcode() methods that are available from Object Class but you have to override them for your purpose.
If a class implements Comparable, that would suggest that instances of the class represent values of some sort; generally, when classes encapsulate values it will be possible for there to exist two distinct instances which hold the same value and should consequently be considered equivalent. Since the only way for distinct object instances to be considered equivalent is for them to override equals and hashCode, that would imply that things which implement Comparable should override equals and hashCode unless the encapsulated values upon which compare operates will be globally unique (implying that distinct instances should never be considered equivalent).
As a simple example, suppose a class includes a CreationRank field of type long; every time an instances is created, that member is set to a value fetched from a singleton AtomicLong, and Comparable uses that field to rank objects in the order of creation. No two distinct instances of the class will ever report the same CreationRank; consequently, the only way x.equals(y) should ever be true is if x and y refer to the same object instance--exactly the way the default equals and hashCode work.
BTW, having x.compare(y) return zero should generally imply that x.equals(y) will return true, and vice versa, but there are some cases where x.equals(y) may be false but x.compare(y) should nonetheless return zero. This may be the case when an object encapsulates some properties that can be ranked and others that cannot. Consider, for example, a hypohetical FutureAction type which encapsulates a DateTime and an implementation of a DoSomething interface. Such things could be ranked based upon the encapsulated date and time, but there may be no sensible way to rank two items which have the same date and time but different actions. Having equals report false while compare reports zero would make more sense than pretending that the clearly-non-equivalent items should be called "equal".

Does it make sense for equals and compareTo to be inconsistent?

I want to make a class usable in SortedSet | SortedMap.
class MyClass implements Comparable<MyClass>{
// the only thing relevant to comparisons:
private final String name;
//...
}
The class' instances must be sorted by their name property.
However, I don't want equally named instances to be considered as equal.
So a SortedSet content would look like a, a, a, b, c.
(Normally, SortedSet would only allow a, b, c)
First of all: is this (philosophically) consistent?
If so, do I have to expect unpredictable behavior, when I don't
override equals(...) and hashCode()?
Edit:
I am sorry, my question seems inconsistent:
I want to put multiple "equal" values inside a set, which doesn't allow this
by concept.
So, please don't reply to my question anymore.
Thanks to all who already replied.
Let me ask you a question: does it make sense to have a.compareTo(b) return 0 and a.equals(b) return false?
I would use a Comparator<MyClass> instead. This is why all SortedMap/SortedSet implementations that I know of allow you to pass in a Comparator at creation.
From the Javadoc for Comparable
It is strongly recommended (though not
required) that natural orderings be
consistent with equals. This is so
because sorted sets (and sorted maps)
without explicit comparators behave
"strangely" when they are used with
elements (or keys) whose natural
ordering is inconsistent with equals
If you want to have compareTo inconsistent with equals(), it is recommended that you instead use an explicit comparator by providing a class that implements Comparator.
If so, do I have to expect unpredictable behavior, when I don't override equals(...) and hashcode()?
You should still override equals() and hashcode(). Whether or not equals() and hashcode() are consistent with compareTo is a different matter.
Effective Java recommends that if you don't implement compareTo consistent with equals you should clearly indicate so:
The recommended language is "Note:
This class has a natural ordering that
is inconsistent with equals."
Just put this code in the equals method and dont ever think about it again:
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof MyClass)) return false;
return 0 == this.compareTo((MyClass) obj);
}

Categories