詳解Java中用于查找對象哈希碼值的hashCode()函數(shù)
理解
hashCode() 的作用是獲取哈希碼,也稱為散列碼;它實際上是返回一個int整數(shù)。這個哈希碼的作用是確定該對象在哈希表中的索引位置。
hashCode() 定義在JDK的Object.java中,這就意味著Java中的任何類都包含有hashCode() 函數(shù)。
雖然,每個Java類都包含hashCode() 函數(shù)。但是,僅僅當(dāng)創(chuàng)建并某個“類的散列表”(關(guān)于“散列表”見下面說明)時,該類的hashCode() 才有用(作用是:確定該類的每一個對象在散列表中的位置;其它情況下(例如,創(chuàng)建類的單個對象,或者創(chuàng)建類的對象數(shù)組等等),類的hashCode() 沒有作用。
上面的散列表,指的是:Java集合中本質(zhì)是散列表的類,如HashMap,Hashtable,HashSet。
也就是說:hashCode() 在散列表中才有用,在其它情況下沒用。在散列表中hashCode() 的作用是獲取對象的散列碼,進而確定該對象在散列表中的位置。
我們都知道,散列表存儲的是鍵值對(key-value),它的特點是:能根據(jù)“鍵”快速的檢索出對應(yīng)的“值”。這其中就利用到了散列碼!
散列表的本質(zhì)是通過數(shù)組實現(xiàn)的。當(dāng)我們要獲取散列表中的某個“值”時,實際上是要獲取數(shù)組中的某個位置的元素。而數(shù)組的位置,就是通過“鍵”來獲取的;更進一步說,數(shù)組的位置,是通過“鍵”對應(yīng)的散列碼計算得到的。
下面,我們以HashSet為例,來深入說明hashCode()的作用。
假設(shè),HashSet中已經(jīng)有1000個元素。當(dāng)插入第1001個元素時,需要怎么處理?因為HashSet是Set集合,它允許有重復(fù)元素。
“將第1001個元素逐個的和前面1000個元素進行比較”?顯然,這個效率是相等低下的。散列表很好的解決了這個問題,它根據(jù)元素的散列碼計算出元素在散列表中的位置,然后將元素插入該位置即可。對于相同的元素,自然是只保存了一個。
由此可知,若兩個元素相等,它們的散列碼一定相等;但反過來確不一定。在散列表中,
1、如果兩個對象相等,那么它們的hashCode()值一定要相同;
2、如果兩個對象hashCode()相等,它們并不一定相等。
注意:這是在散列表中的情況。在非散列表中一定如此!
示例
我們來看一個具體的示例吧,
public class HashTest {
private int i;
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
public int hashCode() {
return i % 10;
}
public final static void main(String[] args) {
HashTest a = new HashTest();
HashTest b = new HashTest();
a.setI(1);
b.setI(1);
Set<HashTest> set = new HashSet<HashTest>();
set.add(a);
set.add(b);
System.out.println(a.hashCode() == b.hashCode());
System.out.println(a.equals(b));
System.out.println(set);
}
}
這個輸出的結(jié)果:
true false [com.ubs.sae.test.HashTest@1, com.ubs.sae.test.HashTest@1]
以上這個示例,我們只是重寫了hashCode方法,從上面的結(jié)果可以看出,雖然兩個對象的hashCode相等,但是實際上兩個對象并不是相等;,我們沒有重寫equals方法,那么就會調(diào)用object默認的equals方法,是比較兩個對象的引用是不是相同,顯示這是兩個不同的對象,兩個對象的引用肯定是不定的。這里我們將生成的對象放到了HashSet中,而HashSet中只能夠存放唯一的對象,也就是相同的(適用于equals方法)的對象只會存放一個,但是這里實際上是兩個對象a,b都被放到了HashSet中,這樣HashSet就失去了他本身的意義了。
此時我們把equals方法給加上:
public class HashTest {
private int i;
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
<span style="color:#3366FF;"><strong>public boolean equals(Object object) {
if (object == null) {
return false;
}
if (object == this) {
return true;
}
if (!(object instanceof HashTest)) {
return false;
}
HashTest other = (HashTest) object;
if (other.getI() == this.getI()) {
return true;
}
return false;
}</strong></span>
public int hashCode() {
return i % 10;
}
public final static void main(String[] args) {
HashTest a = new HashTest();
HashTest b = new HashTest();
a.setI(1);
b.setI(1);
Set<HashTest> set = new HashSet<HashTest>();
set.add(a);
set.add(b);
System.out.println(a.hashCode() == b.hashCode());
System.out.println(a.equals(b));
System.out.println(set);
}
}
此時得到的結(jié)果就會如下:
true true [com.ubs.sae.test.HashTest@1]
從結(jié)果我們可以看出,現(xiàn)在兩個對象就完全相等了,HashSet中也只存放了一份對象。
總結(jié)
1、hashCode的存在主要是用于查找的快捷性,如Hashtable,HashMap等,hashCode是用來在散列存儲結(jié)構(gòu)中確定對象的存儲地址的;
2、如果兩個對象相同,就是適用于equals(java.lang.Object) 方法,那么這兩個對象的hashCode一定要相同;
3、如果對象的equals方法被重寫,那么對象的hashCode也盡量重寫,并且產(chǎn)生hashCode使用的對象,一定要和equals方法中使用的一致,否則就會違反上面提到的第2點;
4、兩個對象的hashCode相同,并不一定表示兩個對象就相同,也就是不一定適用于equals(java.lang.Object) 方法,只能夠說明這兩個對象在散列存儲結(jié)構(gòu)中,如Hashtable,他們“存放在同一個籃子里”。
相關(guān)文章
java基于雙向環(huán)形鏈表解決丟手帕問題的方法示例
這篇文章主要介紹了java基于雙向環(huán)形鏈表解決丟手帕問題的方法,簡單描述了丟手帕問題,并結(jié)合實例形式給出了Java基于雙向環(huán)形鏈表解決丟手帕問題的步驟與相關(guān)操作技巧,需要的朋友可以參考下2017-11-11
MybatisPlusInterceptor實現(xiàn)sql攔截器超詳細教程
這篇文章主要給大家介紹了關(guān)于MybatisPlusInterceptor實現(xiàn)sql攔截器超詳細教程的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學(xué)習(xí)或者工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-08-08
java鏈表應(yīng)用--基于鏈表實現(xiàn)隊列詳解(尾指針操作)
這篇文章主要介紹了java鏈表應(yīng)用--基于鏈表實現(xiàn)隊列,結(jié)合實例形式分析了java基于鏈表實現(xiàn)隊列尾指針相關(guān)操作使用技巧,需要的朋友可以參考下2020-03-03
Java內(nèi)存模型與JVM運行時數(shù)據(jù)區(qū)的區(qū)別詳解
這篇文章主要介紹了Java內(nèi)存模型與JVM運行時數(shù)據(jù)區(qū)的區(qū)別詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-10-10
Java實現(xiàn)線程安全單例模式的五種方式的示例代碼
這篇文章主要介紹了Java中實現(xiàn)線程安全單例模式的五種方式:餓漢式、枚舉單例、懶漢式、DCL懶漢式和靜態(tài)內(nèi)部類懶漢單例,感興趣的可以了解一下2022-03-03

