2
votes

I have a Movie class and I override only hashCode() method. Please find below the java class

public class Movie {

private String actor;
private String name;
private String releaseYr;

public String getActor() {
    return actor;
}

public void setActor(String actor) {
    this.actor = actor;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public String getReleaseYr() {
    return releaseYr;
}

public void setReleaseYr(String releaseYr) {
    this.releaseYr = releaseYr;
}
@Override
    public int hashCode() {
        return actor.hashCode() + name.hashCode() + releaseYr.hashCode();
    }


}

I have created two Movie objects and both the object's all the property values are same and put them in a HashMap. Below is the code

import java.util.HashMap;

public class Test {

public static void main(String[] args) {

    Movie m1 = new Movie();
    m1.setActor("Akshay");
    m1.setName("Taskvir");
    m1.setReleaseYr("2010");

    Movie m2 = new Movie();
    m2.setActor("Akshay");
    m2.setName("Taskvir");
    m2.setReleaseYr("2010");


    HashMap<Movie, String> map = new HashMap<Movie, String>();

    map.put(m1, "Value of m1");
    map.put(m2, "Value of m2");

}

}

I am getting the expected result. As I override only hashCode() method and both the object's hash values are same, so they are stored in the same index location of HashMap table array. Below is the expected result in debugging mode.

enter image description here

But if I do not override the hashCode() method but override the equals() method they are stored in the same index location of HashMap table array. Although I can see the hash values are different. Below is my equals method

@Override
public boolean equals(Object obj) {

    Movie m1 = (Movie) obj;
    boolean result = false;

    if (m1.getActor().equals(this.actor) && m1.getName().equals(this.name)
            && m1.getReleaseYr().equals(this.releaseYr)) {
        result = true;
    }

    return result;
}

The output in debugging mode

enter image description here

If I do not override the equals and hashCode methods then also I am getting same unexpected result.

As per my understanding, if I do not override equals and hashCode methods or only override the equals method then m1 and m2 objects should stored in the different location as hash values are different for m1 and m2 objects. But in this case it is not happening.

Can some one please explain me why with different hash values, my objects stored in the same location?

I have used Java 8.

2

2 Answers

6
votes

Hash codes have a huge range, from Integer.MIN_VALUE to Integer.MAX_VALUE, while a HashMap usually has much fewer buckets (by default, 16 for a newly instantiated HashMap, at least with OpenJDK 11). Thus, it's entirely possible, even expected, that hash codes will collide, and multiple objects will be added to the same bucket. However, note that if you're not overriding hashCode() this behavior is completely incidental, and can't be relied upon.

2
votes

No matter how the hash code is computed, by your method or by the default from the Object class, different objects can get mapped to the same hashmap bucket (array index). The hash code is divided by the array size, and the remainder gives the bucket number.

Both of your hash codes produced by Object.hashCode() (31622540 and 27844196) happen to produce the identical remainder 4 when divided by 16 (the initial HashMap array size).

So, with 4 billion different hash codes available, some of them must end up in the same bucket, as it would be a waste of memory to allocate a 4-billion-elements array for every hash map.

To make the hash map work as expected, it's important that objects that are equal give the same hash code.

If you override only the equals() method, the Object.hashCode() doesn't fulfill that requirement, and you have to override hashCode() as well - otherwise the get() method won't find the objects you stored in the map.

If you want two movies to be equals() if their fields are equal, you should supply an appropriate hashCode() method as well the way you did.

Let's have a look at the possible overriding combinations.

Override nothing

Both movies are different, end up as different hash map entries, maybe in the same, maybe in different buckets.

Only override hashCode()

Both movies are different, end up as different hash map entries in the same bucket. It's nonsense to invent your own hashCode() implementation if you still use the Object definition of equality.

Override both hashCode() and equals()

Both movies are equal, end up as only one hash map entry, with the later-stored value winning. This happens because the second put() finds an entry with an equal key under the hash code's bucket, and simply replaces its value part.

Only override equals()

BIG MISTAKE! Both movies are equal, but this isn't reflected by the hashCode() computation, so it's just a matter of good luck whether the search for an existing value looks into the correct bucket.