Problem in equals() and hashCode() method in java.

public class Experiment {
public static void main(String args[]) {
Student s1 = new Student(“uvw”);
Student s2 = new Student(“xyz”);
HashSet set = new HashSet<>();
set.add(s1);
set.add(s2);
System.out.println(set);
}
}
class Student{

    String name;

    public Student(String name) {this.name=name;}

   /* @Override
    public int hashCode() {
        return name.length();
    }*/

    @Override
    public boolean equals(Object obj) {
        if (name.length()==((Student)obj).name.length())return true;
        return false;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                '}';
    }
}

I am facing some issue with equals() and hashCode() method. What I am trying to do is I want to make HashSet of Student object with unique values. When I remove comments from from commented code my code works as expected. Since I am not using hashCode() method inside equals method there should be no need of hashCode() method in determining whether the objects are equal or not. I suspect my code should work as expected even if I don’t override hashCode() method.

First of all, your code will work and you will be able to perform HashSet operations without implementing hashCode() or equals(). That is because all classes are derived from the Object class and the Object class has implementations of equals() and hashCode(), which all subclasses inherit, including your Student class. Follow the links to learn more about what rules the methods should follow and the default implementations.

The purpose of the equals() function to perform an equality check. Currently your equals() function assumes two Students are equal if the lengths of their names are equal. This is a an unusual assumption, which you probably didn’t intend. Since the only feature a Student has is his name, a better assumption would be that the names are unique and you should check whether their names are equal. If multiple Students may have the same name, you must come up with additional features to distinguish each student.

The purpose the of hashCode() function is to return an integer value based on the properties of the object, in order to distinguish it from other objects. Two objects that compare equal via the equals() method must return the same hash code. However it is allowed for two unequal objects to return the same hash code, although the less this happens the better is the hash function. So the hash code is not a guarantee of equality.

Let’s briefly discuss how the HashSet works.
When you add a Student object s to a HashSet, the HashSet will call the s.hashCode() to get its hash value, and then use that value to store the object in some bucket corresponding to the hash code.
When you try to find a Student s in the HashSet, the HashSet will call s.hashCode() and look in the appropriate bucket. In the bucket, there may be multiple Students that match the hash code, but the HashSet will look for a Student t that also matches s via the equals check.

Now coming back to the behaviour of your code.
1. First of all you need the change your equals() method since it currently judges a Student with name "uvw" and another with "xyz" as equal.
2. If you implement equals() correctly and but leave out hashCode(), the hashCode() no longer relies on the object’s properties. This will violate the rule that “Two objects that compare equal via the equals() method must return the same hash code”. The HashSet will put the object in some bucket. Now if you search for a Student in the set, the set is likely to not find a match even if an equal Student does exist in the set, because it probably will not end up looking in the same bucket. Example case.

So the bottom line is that you should implement equals() and hashCode() that are consistent with each other and follow the specifications provided by the Java language, otherwise the structures dependent on these methods will fail. Hope the explanation was clear :slight_smile:

@meooow

What I didn’t understand is, if I am overriding equals method such that if then length of two student names are equal then two student objects are equal. Then why HashSet is storing both the student object “uvw” and “xyz”.
As both the objects are equal as per the equals method there should be only one getting stored in hashSet().

This is also because the rule of “Two objects that compare equal via the equals() method must return the same hash code” is violated. The two equal objects will be put into two separate buckets in the HashSet if they have different hash codes. If you implement your hashCode(), this does not happen. Try it out.

@meooow

Do you mean set.add(s) internally calls hashCode() before evaluating them with equals method.

Yes, that is correct. Refer to the part of my answer starting at “Let’s briefly discuss how the HashSet works…”.
Perhaps this article on hash tables will help you understand better.

//