所有的對象都繼承自O(shè)bject,equals方法是Object中的public方法肯骇。我們可以重寫它。默認(rèn)的equals方法是比較兩個對象中的內(nèi)存地址叁执,而hashCode的值是根據(jù)對象的地址計算得到的(This is typically implemented by converting the internal address of the object into an integer)捶码。如果你要override equals方法時揉忘,那你也必須要override hashCode方法破停。如果不重寫的話陷嘴,就會違反Object.hashcode的通用約定骇扇,違反的是下面第二條的約定摔竿,如果兩個對象的equals方法比較相同,那么對象的hashCode方法也相同少孝。違反這條約定會導(dǎo)致該類無法結(jié)合所有基于散列的集合一起正常工作继低,這樣的集合包括HashMap、HashSet和Hashtable稍走。
那你可能又會問了袁翁,hashCode怎么就影響了對象在Hash集合中的使用呢?
因為HashMap中有一項優(yōu)化婿脸,可以將與每個項相關(guān)聯(lián)的散列碼緩存起來粱胜,如果散列碼不匹配,也不必檢驗對象的等同性狐树。
int hash = (key == null) ? 0 : sun.misc.Hashing.singleWordWangJenkinsHash(key);
for (HashMapEntry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
下面約定的內(nèi)容是:
- 在應(yīng)用程序執(zhí)行期間焙压,只要對象的equals方法的比較操作所用到的信息沒有修改,那么對同一對象調(diào)用多次抑钟,hashCode方法必須始終如一的返回同一個整數(shù)涯曲。在同一個應(yīng)用程序的多次執(zhí)行過程中,每次執(zhí)行返回的整數(shù)可以不一致在塔。
- 如果兩個對象根據(jù)equals方法比較是相同的幻件,那么調(diào)用這兩個對象中任意一個對象的hashCode方法都必須產(chǎn)生同樣的整數(shù)結(jié)果。
- 如果兩個對象根據(jù)equals方法比較是不相等的心俗,那么調(diào)用這兩個對象中任意一個hashCode方法傲武,則不一定產(chǎn)生不同的整數(shù)結(jié)果蓉驹。但是程序員應(yīng)該知道,給不相等的對象產(chǎn)生截然不同的整數(shù)結(jié)果揪利,有可能提高h(yuǎn)ashtable 的性能态兴。
拓展學(xué)習(xí):Hash表的存儲結(jié)構(gòu)。
理解了Hash表的存儲結(jié)構(gòu)疟位,會對上面的內(nèi)容有更深入的理解瞻润。
哈希表最常實現(xiàn)的方法是拉鏈法,可以理解為鏈表的數(shù)組甜刻,數(shù)組中的每個單元存放的是鏈表的首節(jié)點绍撞。
hashtable.png
那這些元素按照什么樣的規(guī)則存放在數(shù)組中呢?一般情況下通過hash(key)%len獲得得院,也就是元素key的hash值對數(shù)組長度取模得到的傻铣。
這就是為什么第三個約定中說到,給不想等的對象產(chǎn)生截然不同的整數(shù)結(jié)果祥绞,有可能提高h(yuǎn)ashtable的性能非洲。理想情況下,散列函數(shù)應(yīng)該把集合中不相等的實例均勻地分布到所有的散列值中蜕径。