I just spent a few hours debugging a strange bug, whereby I couldn't add some elements to a Set even though no element already contained in the Set, when compared for equality to the element to be added, resulted to be equal. In other words, I could not add an element to a Set, even though the Set did not actually contain the element.
After having printed all the involved variables before insertion, a couple of JUnit runs confirmed that this was indeed the case, while I had spent the previous three hours chasing a bug in my persistence code that was not there, and not even in Hibernate.
Turns out the Set was actually a TreeSet, and the objects contained in the Set implemented the Comparable interface. Looking at the Java docs, I came upon this:
Comparable (Java 2 Platform SE v1.4.2):
The natural ordering for a class C is said to be consistent with equals if and only if
(e1.compareTo((Object)e2) == 0)has the same boolean value ase1.equals((Object)e2)for every e1 and e2 of class C. Note that null is not an instance of any class, ande.compareTo(null)should throw aNullPointerExceptioneven thoughe.equals(null)returnsfalse.
It is strongly recommended (though not required) that natural orderings be consistent withequals. 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. In particular, such a sorted set (or sorted map) violates the general contract for set (or map), which is defined in terms of the equals method.
For example, if one adds two keys a and b such that(!a.equals((Object)b) && a.compareTo((Object)b) == 0)to a sorted set that does not use an explicit comparator, the second add operation returns false (and the size of the sorted set does not increase) because a and b are equivalent from the sorted set's perspective.”
I incorrectly assumed that Set.add checked for equality via the Object.equals method! My compareTo method, on the other hand, used only a subset of the fields used by equals, since the other fields did not matter, as far as sorting was concerned. Well, according to Java, this is wrong.
I should also have remembered Item 11 from Joshua Bloch's Effective Java: “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 collection interfaces (Collection, Set or Map).”
You'll never stop learning!


0 Responses to “Comparable vs. equals()”