hashCode就是我們所說的散列碼,使用hashCode算法可以幫助我們進行高效率的查找,例如HashMap,說hashCode之前,先來看看Object類。
Java程序中所有類的直接或間接父類,處於類層次的最高點。在Object類里定義了很多我們常見的方法,包括我們要講的hashCode方法,如下
- public final native Class<?> getClass();
- public native int hashCode();
- public boolean equals(Object obj) {
- return (this == obj);
- }
- public String toString() {
- return getClass().getName() + "@" + Integer.toHexString(hashCode());
- }
在 java的很多類中都會重寫equals和hashCode方法,這是為什么呢?最常見的String類,比如我定義兩個字符相同的字符串,那么對它們進 行比較時,我想要的結果應該是相等的,如果你不重寫equals和hashCode方法,他們肯定是不會相等的,因為兩個對象的內存地址不一樣。
String類的重寫的hashCode方法
- public int hashCode() {
- int h = hash;
- if (h == 0) {
- int off = offset;
- char val[] = value;
- int len = count;
- for (int i = 0; i < len; i++) {
- h = 31*h + val[off++];
- }
- hash = h;
- }
- return h;
- }
1、這段代碼究竟是什么意思?
其實這段代碼是這個數學表達式的實現
s[i] 是string的第i個字符,n是String的長度。那為什么這里用31,而不是其它數呢?《Effective Java》是這樣說的:之所以選擇31,是因為它是個奇素數,如果乘數是偶數,並且乘法溢出的話,信息就會丟失,因為與2相乘等價於移位運算。使用素數的 好處並不是很明顯,但是習慣上都使用素數來計算散列結果。31有個很好的特性,就是用移位和減法來代替乘法,可以得到更好的性能:31*i==(i<<5)-i。現在的VM可以自動完成這種優化。
2、它返回的hashCode有什么特點呢?
可 以看到,String類是用它的value值作為參數來計算hashCode的,也就是說,相同的value就一定會有相同的hashCode值。這點也 很容易理解,因為value值相同,那么用equals比較也是相等的,equals方法比較相等,則hashCode一定相等。反過來不一定成立。它不 保證相同的hashCode一定有相同的對象。
一個好的hash函數應該是這樣的:為不相同的對象產生不相等的hashCode。
在 理想情況下,hash函數應該把集合中不相等的實例均勻分布到所有可能的hashCode上,要想達到這種理想情形是非常困難的,至少java沒有達到。 因為我們可以看到,hashCode是非隨機生成的,它有一定的規律,就是上面的數學等式,我們可以構造一些具有相同hashCode但value值不一 樣的,比如說:Aa和BB的hashCode是一樣的。
說 到這里,你可能會想,原來構造hash沖突那么簡單啊,那我是不是可以對HashMap函數構造很多<key,value>不都一樣,但具有 相同的hashCode,這樣的話可以把HashMap函數變成一條單向鏈表,運行時間由線性變為平方級呢?雖然HashMap重寫的hashCode方 法比String類的要復雜些,但理論上說是可以這么做的。這也是最近比較熱門的Hash Collision DoS事件。
- public final int hashCode() {
- return (key==null ? 0 : key.hashCode()) ^
- (value==null ? 0 : value.hashCode());
- }
(轉載請注明出處:[url=http://www.k8764.com]博彩通[/url]
總結:字符串hash函數,不僅要減少沖突,而且要注意相同前綴的字符串生成的hash值要相鄰。