11 May 2016

Be aware: Why the HashSet doesn't contain my object?

Let's consider the following dummy class:

class MyClass {

    int uniqueId;

    public void setUniqueId(int uniqueId) {
        this.uniqueId = uniqueId;
    }

    @Override
    public int hashCode() {
        return uniqueId; //Of course you will never do this in real life. Right? :)
    }

    @Override
    public boolean equals(Object obj) {
        return obj.hashCode() == this.hashCode(); //Only to clarify things as well... :)
    }
}

And now, please consider the following snippet:

MyClass myObject = new MyClass();

Set mySet = new HashSet(0);
mySet.add(myObject);
myObject.setUniqueId(1);

myObject.hashCode(); // output: 1
mySet.iterator().next().hashCode(); // output: 1
mySet.contains(myObject.hashCode()); //output: false

People may say that a HashSet.contains() call will fail due to not proper overriding of equals() and hashCode() and they can be right.
Actually they are right most of the times. But here we have a (not so usual, but it happens) situation where the objects' hash are the same and the equals() returns true, but the contains() method still returns false.

How come?
When we go deeper into the Java's HashSet's code, we can figure out that it stores the added object's hash code as a key into a HashMap, so it can lookup later on for that key in a get() operation or, in our situation, to determine whenever the collection contains or not the given object. The problem here is that, when we add the object, it's hash is "0" because we haven't set the unique ID yet (which determines the object's hash code in this example), and that is the information the HashSet will use to locate our object afterwards.
In a nutshell, the HashSet.contains() method makes use of the hash code of an object to locate it and as this hash code is stored in the moment that the object is added to the collection, it will not change. It's a curious situation, cause, as you may add the object again and again after changing it's hash code, the HashSet may contain two or more identical objects inside it! Try it.