小Hub領讀:
雖然是很基礎的一篇文章,但是對于equals、hashcode兩個方法,相信很多人都與其中的規則不熟悉,來跟著小Hub花個8分鐘回顧一下!
作者:不學無數的程序員
https://my.oschina.net/u/4030990/blog/3134199
在?EffectiveJava
中第九條規定在覆蓋?equals()
方法時總要覆蓋?hashCode()
方法。這是為什么呢?接下來我們就介紹一下這兩個方法。
Java中的?equals()
方法和?hashCode()
方法都是在?Object
類中的方法,而在Java中所有的類都是?Obejct
類的子類,所以Java中所有的方法都會有這兩個方法的默認實現。
equals方法
Object
類中的?equals()
方法定義如下
public boolean equals(Object obj) {
return (this == obj);
}
我們發現在?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
類型的哈希值。
public native int hashCode();
在?hashCode()
方法中也有一些規約
如果對象在使用?
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,并且自定義的類不重寫?equals
和?hashCode
方法會發生什么。
自定義的類
@Builder
@NoArgsConstructor
@AllArgsConstructor
class CustomizedKey{
private Integer id;
private String name;
}
接下來我們看使用自定義的類作為key
public static void main(String[] args) {
Map<CustomizedKey, Integer> data = getData();
CustomizedKey key = CustomizedKey.builder().id(1).name("key").build();
Integer integer = data.get(key);
System.out.printf(String.valueOf(integer));
}
private static Map<CustomizedKey,Integer> getData(){
Map<CustomizedKey,Integer> customizedKeyIntegerMap = new HashMap<>();
CustomizedKey key = CustomizedKey.builder().id(1).name("key").build();
customizedKeyIntegerMap.put(key,10);
return customizedKeyIntegerMap;
}
我們可以看到程序最后打印的是一個?null
值。原因正如上面我們說的一樣。
hashCode
:用來計算該對象放入數組中的哪個位置,因為是兩個都是new的對象,所以即使里面的值一樣,但是對象所處的地址卻不同,所以使用默認的?hashCode
也就不同,當然在?hashMap
中就不會認為兩個是一個對象。
接下來我們就重寫一下這兩個方法。如果我們使用?IDEA
的話,那么直接使用快捷鍵即可。
接下來我們看我們實現的兩個方法
@Builder
@NoArgsConstructor
@AllArgsConstructor
class CustomizedKey{
private Integer id;
private String name;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CustomizedKey that = (CustomizedKey) o;
return Objects.equals(id, that.id) &&
Objects.equals(name, that.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
}
然后我們再次運行上面的程序發現輸出打印已經變成了?10
。
我們也能夠使用?
Lombock
提供的?@EqualsAndHashCode
注解簡化代碼
代碼地址:https://github.com/modouxiansheng/Doraemon
(完)
MarkerHub文章索引:
https://github.com/MarkerHub/JavaIndex
【推薦閱讀】
知乎問答:搞開發就怕加班還學不到東西?
很全很牛逼,看完這篇Elasticsearch實戰,我覺得我可以寫個百度~
基于Jwt資源無狀態認證權限管理系統bootshiro
別用Date了,Java8新特性之日期處理,現在學會也不遲!
eblog項目講解視頻上線啦,長達17個小時!!
好文章!點個在看!