秋招面试Java岗,被频繁问到equals和hashcode方法,这里总结一下知识点以供日后回顾。

一、Object中的equals和hashcode方法。

equals和hashcode方法是定义在Java的Object类里的,由于Object类是Java中所有类的根类,所以Java中所有的类都是有equals和hashcode方法的,而且这两个方法大多数时候是会进行重写的,看一下Object类中对这两个方法的定义。

public boolean equals(Object obj) {
        return (this == obj);
    }
public native int hashCode();
  1. 可以看到Object中的equals方法是直接比较引用所指向的地址,但equals方法定义其实是比较的内容,所以很多时候自定义类中都会对equals方法进行重写,比较关键属性是否相同,如String对equals方法的重写是比较其内部字符数组的内容是否相同。
  2. hashcode方法是本地方法,Java中没有提供实现,调用的是C/C++的头文件中的方法,返回值是一个标识这个对象的int值,关于Object.hashcode()的返回值可以参看这篇文章
class People {
    String id;
    String name;

    public People(String id, String name) {
        this.id = id;
        this.name = name;
    }
}

public class CheckHashCode {
    public static void main(String[] args) {
        People p1 = new People("1", "zs");
        People p2 = new People("1", "zs");
        System.out.println(p1.equals(p2));
    }
}

如上代码中,p1和p2对象的内容是相等的,但是由于People类中没有重写equals方法,所以使用的是继承自Object类的equals方法进行的判断,判断p1和p2对象的内存地址是否相等,结果当然是不相等,返回为false;
加入equals后,再进行判断返回为true.

class People {
    String id;
    String name;

    public People(String id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof People)) return false;
        People people = (People) o;
        return Objects.equals(id, people.id) &&
                Objects.equals(name, people.name);
    }
}

public class CheckHashCode {
    public static void main(String[] args) {
        People p1 = new People("1", "zs");
        People p2 = new People("1", "zs");
        System.out.println(p1.equals(p2));
    }
}

二、重写了equals后必须要重写hashcode方法吗?

经常看到这样一句话,要求重写equals方法后必须要重写hashcode方法,然而之前没有深入细究过这个问题,面试的时候就被卡住了。
hashcode是可以达到O(1)的直接映射的,当然需要解决哈希碰撞等问题。进行集合遍历时,如果元素非常多,使用equals逐个去遍历的化效率就会非常低,直接使用hashcode就可以直接一次性映射;此外,对象在容器中如果被用到的话,需要重写hashcode,以HashMap为例,如果不覆写hashcode方法的话,就默认使用Object的hashcode方法,那么对于两个不同的对象,即使其关键属性的值相同(即equals的结果为true),其hashcode值也不会相同,而HashMap的数据结构是一个table数组,table数组中的每一个元素称为一个哈希槽,每个哈希槽引出的是一个哈希桶,哈希桶中的是链表或者红黑树。
这样就会出现即使两个对象equals结果为true,由于hashcode值不同,在执行HashMap的put方法是会将这两个对象放到不同的哈希桶中,因此在这种自定义类而且会被用作容器内元素的场景下,在重写了equals方法后必须重写hashcode方法。

import java.util.HashMap;
import java.util.Objects;

//People没有覆写hashcode方法,其hashcode值默认是Object中hashcode方法的返回值——对象的地址!
class People {
    String id;
    String name;

    public People(String id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof People)) return false;
        People people = (People) o;
        return Objects.equals(id, people.id) &&
                Objects.equals(name, people.name);
    }
}

public class CheckHashCode {
    public static void main(String[] args) {
        People p1 = new People("1", "zs");
        People p2 = new People("1", "zs");
        HashMap<People, Integer> peopleIntegerHashMap = new HashMap<>();
        peopleIntegerHashMap.put(p1, peopleIntegerHashMap.getOrDefault(p1, 0) + 1);
        //输出结果为false
        System.out.println(peopleIntegerHashMap.containsKey(p2));
    }
}

总结,equals方法重写了之后如果用不到hashcode放到话其实不必重写,但是你无法预知会不会用到,例如可能作为HashMap的key,所以重写了equals方法后还是重写hashcode吧,不要给自己和别人找无谓的麻烦!