為什么重寫equals必須重寫hashCode

equals常見面試題

在開始聊之前,我們先看幾個常見的面試題,看看你能不能都回答上來。

  • 1、equals和==有什么區(qū)別?
  • 2、hashcode相等的兩個對象一定==相等嗎?equals相等嗎?
  • 3、兩個對象用equals比較相等,那它們的hashcode相等嗎?

如果我們不重寫equals和hashcode,那么它使用的是Object方法的實現(xiàn)。我們先簡單看一下

public boolean equals(Object obj) {
    return (this == obj);
}
public static int hashCode(Object o) {
    return o != null ? o.hashCode() : 0;
}

為什么要重寫equals

通過以上代碼可以看出,Object提供的equals在進行比較的時候,并不是進行值比較,而是內(nèi)存地址的比較。由此可以知曉,要使用equals對對象進行比較,那么就必須進行重寫equals。

重寫equals不重寫hashCode會存在什么問題

我們先看下面這段話

每個覆蓋了equals方法的類中,必須覆蓋hashCode。如果不這么做,就違背了hashCode的通用約定,也就是上面注釋中所說的。進而導(dǎo)致該類無法結(jié)合所以與散列的集合一起正常運作,這里指的是HashMap、HashSet、HashTable、ConcurrentHashMap。
來自 Effective Java 第三版

結(jié)論:如果重寫equals不重寫hashCode它與散列集合無法正常工作。

既然這樣那我們就拿我們最熟悉的HashMap來進行演示推導(dǎo)吧。我們知道HashMap中的key是不能重復(fù)的,如果重復(fù)添加,后添加的會覆蓋前面的內(nèi)容。那么我們看看HashMap是如何來確定key的唯一性的。

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

查看代碼發(fā)現(xiàn),它是通過計算Map key的hashCode值來確定在鏈表中的存儲位置的。那么這樣就可以推測出,如果我們重寫了equals但是沒重寫hashCode,那么可能存在元素重復(fù)的矛盾情況。

下面我們來演示一下

public class Employee {

private String name;

private Integer age;

public Employee(String name, Integer age) {
    this.name = name;
    this.age = age;
}

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Employee employee = (Employee) o;
    return Objects.equals(name, employee.name) &&
            Objects.equals(age, employee.age);
}

/*@Override
public int hashCode() {
    return Objects.hash(name, age);
}*/
}
public static void main(String[] args) {

    Employee employee1 = new Employee("冰峰", 20);
    Employee employee2 = new Employee("冰峰", 22);
    Employee employee3 = new Employee("冰峰", 20);

    HashMap<Employee, Object> map = new HashMap<>();

    map.put(employee1, "1");
    map.put(employee2, "1");
    map.put(employee3, "1");

    System.out.println("equals:" + employee1.equals(employee3));
    System.out.println("hashCode:" + (employee1.hashCode() == employee3.hashCode()));
    System.out.println(JSONObject.toJSONString(map));
}

按正常情況來推測,map中只存在兩個元素,employee2和employee3。

執(zhí)行結(jié)果

image.png

出現(xiàn)這種問題的原因就是因為沒有重寫hashCode,導(dǎo)致map在計算key的hash值的時候,絕對值相同的對象計算除了不一致的hash值。


接下來我們打開hashCode的注釋代碼,看看執(zhí)行結(jié)果

image.png

總結(jié)

如果重寫了equals就必須重寫hashCode,如果不重寫將引起與散列集合(HashMap、HashSet、HashTable、ConcurrentHashMap)的沖突。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容