The doc about java.util.Set.contains(Object o)
says:
Returns true if and only if this set contains an element e such that (o==null ? e==null : o.equals(e)).
That said, here is a POJO (as you can see, I overwrote its equals
method):
public class MonthAndDay {
private int month;
private int day;
public MonthAndDay(int month, int day) {
this.month = month;
this.day = day;
}
@Override
public boolean equals(Object obj) {
MonthAndDay monthAndDay = (MonthAndDay) obj;
return monthAndDay.month == month && monthAndDay.day == day;
}
}
So please, why does the following code prints false
instead of true
?
Set<MonthAndDay> set = new HashSet<MonthAndDay>();
set.add(new MonthAndDay(5, 1));
System.out.println(set.contains(new MonthAndDay(5, 1)));
// prints false
A solution is to rewrite the contains(Object o)
method, but the original one should be (almost) exactly the same, am I wrong?
Set<MonthAndDay> set = new HashSet<MonthAndDay>() {
private static final long serialVersionUID = 1L;
@Override
public boolean contains(Object obj) {
MonthAndDay monthAndDay = (MonthAndDay) obj;
for (MonthAndDay mad : this) {
if (mad.equals(monthAndDay)) {
return true;
}
}
return false;
}
};
set.add(new MonthAndDay(5, 1));
System.out.println(set.contains(new MonthAndDay(5, 1)));
// prints true
When you override
equals(Object)
you also need to overridehashcode()
.Specifically, the methods must be implemented so that if
a.equals(b)
istrue
, thena.hashcode() == b.hashcode()
is alltrue
. If this invariant is not respected, thenHashMap
,HashSet
andHashtable
will not work properly.The technical details of how
hashcode()
andequals(Object)
should behave are specified in the Object API.So why do the hash-based data structures break if you get this wrong? Well basically because a hash table works by using the value of the hash function to narrow down the set of values to be compared with the "candidate". If the hashcode for the candidate is different to the hashcode for some object in the table, then the chances are that the lookup algorithm won't compare with the object in the table ... even if the objects are equal.
HashSet
will only useequals()
if the elements share the samehashCode()
, thus you need to override both. Here's the relevant part of the code that is used byHashSet#contains()
(note thatHashSet
is backed by aHashMap
):Not doing so, violates the contract of
Object#hashCode()
, which states that: