爲何重寫了equals()也要重寫hashCode()

爲何重寫了equals()也要重寫hashCode()

筆者文筆功力尚淺,若有不妥,請慷慨指出,一定感激涕零java

Effective Java中第九條規定在覆蓋equals()方法時總要覆蓋hashCode()方法。這是爲何呢?接下來咱們就介紹一下這兩個方法。git

Java中的equals()方法和hashCode()方法都是在Object類中的方法,而在Java中全部的類都是Obejct類的子類,因此Java中全部的方法都會有這兩個方法的默認實現。github

equals方法

Object類中的equals()方法定義以下web

1public boolean equals(Object obj) {
2    return (this == obj);
3}
複製代碼

咱們發如今equals()方法中就關鍵的==,那麼==在Java中有什麼含義呢,咱們都知道在Java中分爲基本數據類型和引用數據類型。那麼==在這兩個類型中做用是不同的。面試

  • 基本數據類型:比較的是==兩邊值是否相等
  • 引用數據類型:比較的是==兩邊內存地址是否相等

基本數據類型包括:byte,short,char,int,long,float,double,boolean算法

而經過Java文檔中的equals()方法描述,全部要實現本身的equals()方法都要遵照下面幾個規則後端

  • 自反性:對於任何對象x,x.equals(x)應該返回true
  • 對稱性:對於任何兩個對象x和y,若是x.equals(y)返回true,那麼y.equals(x)也應該返回true
  • 傳遞性:對於多個對象x,y,z,若是x.equals(y)返回true,y.equals(z)返回true,那麼y.equals(z)也應該返回true
  • 一致性:對於兩個非空對象x,y,在沒有修改此對象的前提下,屢次調用返回的結果應該相同
  • 對於任何非空的對象x,x.equals(null)都應該返回false

hashCode方法

Object中的hashCode()方法是一個本地方法,返回一個int類型的哈希值。數組

1public native int hashCode();
複製代碼

hashCode()方法中也有一些規約app

  • 若是對象在使用equals方法中進行比較的參數沒有修改,那麼屢次調用一個對象的hashCode()方法返回的哈希值應該是相同的。
  • 若是兩個對象經過equals方法比較是相等的,那麼要求這兩個對象的hashCode方法返回的值也應該是相等的。
  • 若是兩個對象經過equals方法比較是不一樣的,那麼也不要求這兩個對象的hashCode方法返回的值是相同的。可是咱們應該知道對於不一樣對象產生不一樣的哈希值對於哈希表(HashMap等等)可以提升性能。

equals方法和hashCode方法會在哪用到

這兩個方法常常出如今Java中的哪一個類裏面呢?若是看過HashMap源碼的應該瞭解這兩個方法常常出如今HashMap中。網上介紹HashMap類的文章有不少了,這裏就簡單介紹一下HashMap框架

當一個節點中的鏈表超過了8的時候就會變爲紅黑樹,以解決鏈表長度過長之後查詢速度慢的缺點。

HashMap是由數組和鏈表組成的高效存儲數據的結構。那麼是如何肯定一個數據存儲在數組中的哪一個位置呢?就是經過hashCode方法進行計算出存儲在哪一個位置,還記得咱們上面講hashCode方法說了有可能兩個不一樣對象的hashCode方法返回的值相同,那麼此時就會產生衝突,產生衝突的話就會調用equals方法進行比對,若是不一樣,那麼就將其加入鏈表尾部,若是相同就替換原數據。

計算位置固然不是上面簡單的一個hashCode方法就計算出來,中間還有一些其餘的步驟,這裏能夠簡單的認爲是hashCode肯定了位置。

何時去覆蓋這兩個方法呢?

若是你不將自定義的類定義爲HashMap的key值的話,那麼咱們重寫了equals方法而沒有重寫hashCode方法,編譯器不會報任何錯,在運行時也不會拋任何異常。

若是你想將自定義的類定義爲HashMap的key值得話,那麼若是重寫了equals方法那麼就必須也重寫hashCode方法。

接下來咱們能夠看一下咱們使用自定義的類做爲HashMap的key,而且自定義的類不重寫equalshashCode方法會發生什麼。

自定義的類

1@Builder
2@NoArgsConstructor
3@AllArgsConstructor
4class CustomizedKey{
5    private Integer id;
6    private String name;
7}
複製代碼

接下來咱們看使用自定義的類做爲key

 1    public static void main(String[] args{
2
3        Map<CustomizedKey, Integer> data = getData();
4
5        CustomizedKey key = CustomizedKey.builder().id(1).name("key").build();
6
7        Integer integer = data.get(key);
8
9        System.out.printf(String.valueOf(integer));
10    }
11
12    private static Map<CustomizedKey,Integer> getData(){
13        Map<CustomizedKey,Integer> customizedKeyIntegerMap = new HashMap<>();
14        CustomizedKey key = CustomizedKey.builder().id(1).name("key").build();
15        customizedKeyIntegerMap.put(key,10);
16        return customizedKeyIntegerMap;
17    }
複製代碼

咱們能夠看到程序最後打印的是一個null值。緣由正如上面咱們說的同樣。

  • hashCode:用來計算該對象放入數組中的哪一個位置,由於是兩個都是new的對象,因此即便裏面的值同樣,可是對象所處的地址卻不一樣,因此使用默認的hashCode也就不一樣,固然在hashMap中就不會認爲兩個是一個對象。

接下來咱們就重寫一下這兩個方法。若是咱們使用IDEA的話,那麼直接使用快捷鍵便可。

接下來咱們看咱們實現的兩個方法

 1@Builder
2@NoArgsConstructor
3@AllArgsConstructor
4class CustomizedKey{
5    private Integer id;
6    private String name;
7
8    @Override
9    public boolean equals(Object o) {
10        if (this == o) return true;
11        if (o == null || getClass() != o.getClass()) return false;
12        CustomizedKey that = (CustomizedKey) o;
13        return Objects.equals(id, that.id) &&
14                Objects.equals(name, that.name);
15    }
16
17    @Override
18    public int hashCode() {
19        return Objects.hash(id, name);
20    }
21}
複製代碼

而後咱們再次運行上面的程序發現輸出打印已經變成了10

咱們也可以使用Lombok提供的@EqualsAndHashCode註解簡化代碼

本文代碼地址

往期文章

參考文章

相關文章
相關標籤/搜索