gene b.
gene b.

Reputation: 12056

Calling hashCode() from equals()

I have defined hashCode() for my class, with a lengthy list of class attributes.

Per the contract, I also need to implement equals(), but is it possible to implement it simply by comparing hashCode() inside, to avoid all the extra code? Are there any dangers of doing so?

e.g.

@Override
public int hashCode() 
{
    return new HashCodeBuilder(17, 37)
        .append(field1)
        .append(field2)
    // etc.
    // ...
}

@Override
public boolean equals(Object that) {
    // Quick special cases
    if (that == null) {
        return false;
    }
    if (this == that) {
        return true;
    }
    // Now consider all main cases via hashCode()
    return (this.hashCode() == that.hashCode());
}

Upvotes: 5

Views: 1644

Answers (2)

Anderson Vieira
Anderson Vieira

Reputation: 9069

Don't do that.

The contract for hashCode() says that two objects that are equal must have the same hashcode. It doesn't guarantee anything for objects that are not equal. What this means is that you could have two objects that are completely different but, by chance, happen to have the same hashcode, thus breaking your equals().

It is not hard to get hashcode collisions between strings. Consider the core loop from the JDK 8 String.hashCode() implementation:

for (int i = 0; i < value.length; i++) {
    h = 31 * h + val[i];
}

Where the initial value for h is 0 and val[i] is the numerical value for the character in the ith position in the given string. If we take, for example, a string of length 3, this loop can be written as:

h = 31 * (31 * val[0] + val[1]) + val[2];

If we choose an arbitrary string, such as "abZ", we have:

h("abZ") = 31 * (31 * 'a' + 'b') + 'Z'
h("abZ") = 31 * (31 * 97 + 98) + 90
h("abZ") = 96345

Then we can subtract 1 from val[1] while adding 31 to val[2], which gives us the string "aay":

h("aay") = 31 * (31 * 'a' + 'a') + 'y'
h("aay") = 31 * (31 * 97 + 97) + 121
h("aay") = 96345

Resulting in a collision: h("abZ") == h("aay") == 96345.

Also, note that your equals() implementation does not check if you are comparing objects of the same type. So, supposing you had this.hashCode() == 96345, the following statement would return true:

yourObject.equals(Integer.valueOf(96345))

Which is probably not what you want.

Upvotes: 14

isnot2bad
isnot2bad

Reputation: 24464

It is definitely not safe to just compare the hashCode() of your objects.

Your objects can have more different states than hash codes: Hash code is an int, that means it is limited to 2^32 = 4,294,967,296 possible values, but your object will probably have more than one single int field.

So it is proven, that there might be two different objects (according to equals) that have the same hash code.

But of course, you can first compare the hash codes for performance reasons (if hash code computation is faster than field comparison): If the hash codes are not equal, the objects are unequal too, so you can safely return false immediately!

Upvotes: 3

Related Questions