hashcode()和hash()
1 為什么有hashcode()方法
equals()和hashcode()這兩個方法都是從object類中繼承過來的。
hashcode() 方法,在object類中定義如下:
public native int hashCode();native說明是一個本地方法,它的實現(xiàn)是根據(jù)本地機器相關的。當然我們可以在自己寫的類中覆蓋hashcode()方法,比如String、Integer、Double。。。。等等這些類都是覆蓋了hashcode()方法的。
例如String類中:就是以31為權,每一位為字符的ASCII值進行運算,用自然溢出來等效取模。(為什么取31?主要是因為31是一個奇質(zhì)數(shù),所以31i = 32i - i = (i << 5) - i,這種位移與減法結合的計算相比一般的運算快很多)。
Integer類的:
public static int hashCode(int value) {return value;}Double類的
public static int hashCode(double value) {long bits = doubleToLongBits(value);return (int)(bits ^ (bits >>> 32));}hashCode對于List集合、數(shù)組而言,他就是一個累贅,但是對HashMap、HashSet、HashTable而言,它變得異常重要。hashCode是用來在散列存儲結構中確定對象的存儲地址的。
考慮一種情況,當向集合中插入對象時,如何判別在集合中是否已經(jīng)存在該對象了?(注意:集合中不允許重復的元素存在)也許大多數(shù)人都會想到調(diào)用equals方法來逐個進行比較,這個方法確實可行。但是如果集合中已經(jīng)存在一萬條數(shù)據(jù)或者更多的數(shù)據(jù),如果采用equals方法去逐一比較,效率必然是一個問題。此時hashCode方法的作用就體現(xiàn)出來了,當集合要添加新的對象時,先調(diào)用這個對象的hashCode方法,得到對應的hashcode值,實際上在HashMap的具體實現(xiàn)中會用一個table保存已經(jīng)存進去的對象的hashcode值,如果table中沒有該hashcode值,它就可以直接存進去,不用再進行任何比較了;如果存在該hashcode值, 就調(diào)用它的equals方法與新元素進行比較,相同的話就不存了,不相同就散列其它的地址,所以這里存在一個沖突解決的問題,這樣一來實際調(diào)用equals方法的次數(shù)就大大降低了,說通俗一點:Java中的hashCode方法就是根據(jù)一定的規(guī)則將與對象相關的信息(比如對象的存儲地址,對象的字段等)映射成一個數(shù)值,這個數(shù)值稱作為散列值。
2 hashCode與equals
如果x.equals(y)返回“true”,那么x和y的hashCode()必須相等。
如果x.equals(y)返回“false”,那么x和y的hashCode()有可能相等,也有可能不等。
3 HashMap中的hash()
hashmap中要找到某個元素,需要根據(jù)key的hash值來求得對應數(shù)組中的位置.
key的hash值高16位不變,低16位與高16位異或作為key的最終hash值。(h >>> 16,表示無符號右移16位,高位補0,任何數(shù)跟0異或都是其本身,因此key的hash值高16位不變。)
為什么要這么干呢? 這個與HashMap中table下標的計算有關。
n = table.length; index = (n-1) & hash;當 length 總是 2 的倍數(shù)時,h & (length-1)將是一個非常巧妙的設計:
假設 h=5,length=16, 那么 h & length - 1 將得到 5;
如果 h=6,length=16, 那么 h & length - 1 將得到 6 ……
如果 h=15,length=16, 那么 h & length - 1 將得到 15;
但是當 h=16 時 , length=16 時,那么 h & length - 1 將得到 0 了;
當 h=17 時 , length=16 時,那么 h & length - 1 將得到 1 了……
這樣保證計算得到的索引值總是位于 table 數(shù)組的索引之內(nèi)。
HashMap的初始大小和擴容都是以2的次方來進行的,換句話說length-1換成二進制永遠是全部為1,比如容量為16,則length-1為1111,大家知道位運算&的規(guī)則是兩個1才得1,遇0得0,也就是說length-1中的某一位為1,則對應位置的計算結果只取決于h中的對應位置(h中對應位取0,則對應位結果為0,h對應位取1,則對應位結果為1。這樣就有兩個結果),但是如果length-1中某一位為0,則不論h中對應位的數(shù)字為幾,對應位結果都是0,這樣就讓兩個h取到同一個結果,這就是hash沖突了,恰恰length-1又是全部為1的數(shù),所以結果自然就將hash沖突最小化了
總之:
1.length(2的整數(shù)次冪)的特殊性導致了length-1的特殊性(二進制全為1)
2.位運算快于十進制運算,hashmap擴容也是按位擴容
4 HashTable中的hash()
int hash = key == null ? 0 : key.hashCode() & 0x7FFFFFFF; int index = hash % table.length;5 ConcurrentHashMap中的hash()
// 用于和負數(shù)hash值進行 & 運算,將其轉化為正數(shù)(絕對值不相等),Hashtable中定位hash桶也有使用這種方式來進行負數(shù)轉正數(shù)static final int HASH_BITS = 0x7fffffff;int hash = spread(key.hashCode());int index = (n - 1) & hash;static final int spread(int h) {return (h ^ (h >>> 16)) & HASH_BITS;}
作者:時間的禮盒
鏈接:https://www.jianshu.com/p/b9558ad35f70
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯(lián)系作者獲得授權并注明出處。
總結
以上是生活随笔為你收集整理的hashcode()和hash()的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java8 HashMap源码分析
- 下一篇: 利用策略模式优化过多 if else 代