首先我們來了解一下hashcode,什麼是hashcode?有什麼作用?
hashcode其實就是散列碼,hashcode使用高效率的哈希算法來定位查找對象!
我們在使用容器來存儲數據的時候會計算一串散列碼,然後將數據放入容器。
如:String s =“java”,那麼計算機會先計算散列碼,然後放入相應的數組中,數組的索引就是從散列碼計算來的,然後再裝入數組裏的容器裏,如List.這就相當於把你要存的數據分成了幾個大的部分,然後每個部分存了很多值, 你查詢的時候先查大的部分,再在大的部分裏面查小的,這樣就比先行查詢要快很多!
一個對象的HashCode就是一個簡單的Hash算法的實現,雖然它和那些真正的複雜的Hash算法相比還不能叫真正的算法,但如何實現它,不僅僅是程序員的編程水平問題, 而是關係到你的對象在存取時性能的非常重要的問題.有可能,不同HashCode可能 會使你的對象存取產生成百上千倍的性能差別!
java String在打印這個類型的實例對象的時候總是顯示爲下面的形式
test.Test$tt@c17164
上面test.Test是類名$tt是我自己寫的內部類,而@後面這一段是什麼呢?他其實就是tt這個實例類的hashcode的16進制!
它使用了Object 裏面的toString()方法
- return getClass().getName() + “@” + Integer.toHexString(hashCode());
- public int hashCode() {
- int h = hash;
- if (h == 0 && value. length > 0) {
- char val[] = value;
- for ( int i = 0; i < value. length; i++) {
- h = 31 * h + val[i];
- }
- hash = h;
- }
- return h;
- }
public int hashCode() {
int h = hash;
if (h == 0 && value. length > 0) {
char val[] = value;
for ( int i = 0; i < value. length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
Java代碼
- s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
- String str = "abcd";
- h = 0
- value.length = 4
- val[0] = a
- val[1] = b
- val[2] = c
- val[3] = d
- h = 31*0 + a
- = a
- h = 31 * (31*0 + a) + b
- = 31 * a + b
- h = 31 * (31 * (31*0 + a) + b) + c
- = 31 * (31 * a + b) + c
- = 31 * 31 * a + 31 * b + c
- h = 31 * (31 * 31 * a + 31 * b + c) + d
- = 31 * 31 * 31 * a + 31 * 31 * b + 31 * c + d
- h = 31 ^ (n-1) * val[0] + 31 ^ (n-2) * val[1] + 31 ^ (n-3) * val[2] + ...+ val[n-1]
String str = "abcd";
h = 0
value.length = 4
val[0] = a
val[1] = b
val[2] = c
val[3] = d
h = 31*0 + a
= a
h = 31 * (31*0 + a) + b
= 31 * a + b
h = 31 * (31 * (31*0 + a) + b) + c
= 31 * (31 * a + b) + c
= 31 * 31 * a + 31 * b + c
h = 31 * (31 * 31 * a + 31 * b + c) + d
= 31 * 31 * 31 * a + 31 * 31 * b + 31 * c + d
h = 31 ^ (n-1) * val[0] + 31 ^ (n-2) * val[1] + 31 ^ (n-3) * val[2] + ...+ val[n-1]
大家都知道,計算機的乘法涉及到移位計算。當一個數乘以2時,就直接拿該數左移一位即可!選擇31原因是因爲31是一個素數!
所謂素數:
質數又稱素數。指在一個大於1的自然數中,除了1和此整數自身外,沒法被其他自然數整除的數。
在存儲數據計算hash地址的時候,我們希望儘量減少有同樣的hash地址,所謂“衝突”。如果使用相同hash地址的數據過多,那麼這些數據所組成的hash鏈就更長,從而降低了查詢效率!所以在選擇係數的時候要選擇儘量長(31 = 11111[2])的係數並且讓乘法儘量不要溢出(如果選擇大於11111的數,很容易溢出)的係數,因爲如果計算出來的hash地址越大,所謂的“衝突”就越少,查找起來效率也會提高。
31可以 由i*31== (i<<5)-1來表示,現在很多虛擬機裏面都有做相關優化,使用31的原因可能是爲了更好的分配hash地址,並且31只佔用5bits!
在java乘法中如果數字相乘過大會導致溢出的問題,從而導致數據的丟失.
而31則是素數(質數)而且不是很長的數字,最終它被選擇爲相乘的係數的原因不過與此!