筆者文筆功力尚淺,若有不妥,請慷慨指出,一定感激涕零java
在Effective Java
中第九條規定在覆蓋equals()
方法時總要覆蓋hashCode()
方法。這是爲何呢?接下來咱們就介紹一下這兩個方法。git
Java中的equals()
方法和hashCode()
方法都是在Object
類中的方法,而在Java中全部的類都是Obejct
類的子類,因此Java中全部的方法都會有這兩個方法的默認實現。github
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.equals(x)
應該返回true
x.equals(y)
返回true
,那麼y.equals(x)
也應該返回true
x.equals(y)
返回true
,y.equals(z)
返回true
,那麼y.equals(z)
也應該返回true
x.equals(null)
都應該返回false
Object
中的hashCode()
方法是一個本地方法,返回一個int
類型的哈希值。數組
1public native int hashCode();
複製代碼
在hashCode()
方法中也有一些規約app
equals
方法中進行比較的參數沒有修改,那麼屢次調用一個對象的hashCode()
方法返回的哈希值應該是相同的。equals
方法比較是相等的,那麼要求這兩個對象的hashCode
方法返回的值也應該是相等的。equals
方法比較是不一樣的,那麼也不要求這兩個對象的hashCode
方法返回的值是相同的。可是咱們應該知道對於不一樣對象產生不一樣的哈希值對於哈希表(HashMap等等)可以提升性能。這兩個方法常常出如今Java中的哪一個類裏面呢?若是看過HashMap
源碼的應該瞭解這兩個方法常常出如今HashMap
中。網上介紹HashMap
類的文章有不少了,這裏就簡單介紹一下HashMap
。框架
當一個節點中的鏈表超過了8的時候就會變爲紅黑樹,以解決鏈表長度過長之後查詢速度慢的缺點。
HashMap
是由數組和鏈表組成的高效存儲數據的結構。那麼是如何肯定一個數據存儲在數組中的哪一個位置呢?就是經過hashCode
方法進行計算出存儲在哪一個位置,還記得咱們上面講hashCode
方法說了有可能兩個不一樣對象的hashCode
方法返回的值相同,那麼此時就會產生衝突,產生衝突的話就會調用equals
方法進行比對,若是不一樣,那麼就將其加入鏈表尾部,若是相同就替換原數據。
計算位置固然不是上面簡單的一個
hashCode
方法就計算出來,中間還有一些其餘的步驟,這裏能夠簡單的認爲是hashCode
肯定了位置。
若是你不將自定義的類定義爲HashMap
的key值的話,那麼咱們重寫了equals
方法而沒有重寫hashCode
方法,編譯器不會報任何錯,在運行時也不會拋任何異常。
若是你想將自定義的類定義爲HashMap
的key值得話,那麼若是重寫了equals
方法那麼就必須也重寫hashCode
方法。
接下來咱們能夠看一下咱們使用自定義的類做爲HashMap
的key,而且自定義的類不重寫equals
和hashCode
方法會發生什麼。
自定義的類
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
註解簡化代碼