Why should the hashCode method be overridden when the equals method is overridden?

Posted by mdinowitz on Thu, 06 Jan 2022 01:59:50 +0100

Everyone should have encountered this problem during the interview, that is, if I rewrite the equals method, why do I have to rewrite the hashCode method?

Many people may not have rewritten hashCode and equals methods, because in practical applications, few people will directly use custom objects as key s. Therefore, the rewriting of hashCode and equals methods actually belongs to business requirements, not technical requirements.

If you read the explanations of these two methods in the Object class, you may also notice that there is a sentence above the comments of the equals method: Please note that whenever you rewrite this method, you usually need to rewrite the hashCode method to maintain the general convention of the hashCode method, that is, equal objects must have equal hash codes.

Therefore, you can see that technically, the hashCode method does not have to be rewritten, but if it is not rewritten, it does not comply with the general convention of the hashCode method.

Therefore, this method has been standardized in terms of constraints. Many later things are based on this specification. For example, our commonly used Map set uses the hashCode method of the corresponding key, because the position of the key is calculated according to the hashCode.

So why rewrite these two methods? That is, if your business does not have this requirement, you should not rewrite it. If it does have this requirement, remember to rewrite the hashCode while rewriting the equals method.

If you are still confused, let's give a simple example. For example, I have a class called User, and the code is as follows:

public class User{

    //full name
    private String name;
    //ID card No.
    private String idcard;

}

For simplicity, there are only two fields, one name and one ID number. Normally, each of us has the unique ID number, right? So if there is a requirement in business that the ID number is the same, then the two are considered to be the same object. Then the code becomes as follows:

public class User{

    //full name
    private String name;
    //ID card No.
    private String idcard;

    public User(String name, String idcard) {
        this.name = name;
        this.idcard = idcard;
    }

    @Override
    public boolean equals(Object obj) {
        if(this==obj){
            return true;
        }
        //Is not an instance of the User class
        if(!(obj instanceof User)){
            return false;
        }
        User other = (User) obj;
        //If the ID cards are empty, they are also considered equal
        if(this.idcard==null&&other.idcard==null){
            return true;
        }
        return this.idcard!=null && other.idcard!=null && this.idcard.equals(other.idcard);
    }

}

Is it difficult? Let's write the test code and see what the effect will be?

public static void main(String[] args) {
        String idcard = "123";
        String name = "zhangsan";
        User user = new User(name,idcard);
        User user1 = new User(name,idcard);
        System.out.println(user.equals(user1));
        System.out.println("user1 hashCode:"+user.hashCode()+"---user1 hashCode:"+user1.hashCode());
    }

The results are as follows:

true
user1 hashCode:972765878---user1 hashCode:1651945012

You can see that the two objects are equal, but the hashcodes are unequal. You may think that there is no problem. That is, the hashcodes are not equal.

Indeed, as long as you don't use hashCode, it's at most a violation of the protocol. If you use Map and use User as the key, you will have a big problem.

Suppose there is a business that counts the number of visits of users. When we use Map like this, we can see that 100 records are finally printed. In fact, only two people visit, namely zhangsan and lisi.

public static void main(String[] args) {
        String[] idcards = {"123","124"};
        String[] names = {"zhangsan","lisi"};
        Map<User, AtomicLong> userVisitMap = new HashMap<>();

        for (int i=0;i<100;i++){
            int index = i% idcards.length;
            String idcard = idcards[index];
            String name = idcards[index];
            User key = new User(name,idcard);
            if(userVisitMap.containsKey(key)){
                userVisitMap.get(key).getAndIncrement();
            }else{
                userVisitMap.put(key,new AtomicLong(1));
            }
        }
        userVisitMap.entrySet().forEach((e)->System.out.println(e.getKey()+"--"+e.getValue()));
    }

You may also notice that this key is new every time, and it is normal to print it 100 times. But don't forget, we have rewritten the equals method. As long as its ID cards are equal, it is the same person, right? So this has nothing to do with how many new, so what's the problem? Yes, but the hashCode method is not rewritten.

Simply rewrite hashCode

    @Override
    public int hashCode() {
        return Objects.hash(idcard);
    }

At this time, let's run the above example again. At this time, you will get the following results:

com.hy.user.User@be51--50
com.hy.user.User@be52--50

This is in line with the actual business that we only have two user requests.

Through this example, you should understand that these two methods do not have to be rewritten, because the interview basically asks why hashCode must be rewritten if equals is rewritten? If equals is overridden.

So I hope you won't be forced to rewrite these two methods according to your business needs.

Topics: Java Interview linked list