HashCode理解,應用,重寫Hashcode


HashCode 意義:

 總的來說,Java中的集合(Collection)有兩類,一類是List,再有一類是Set。你知道它們的區別嗎?前者集合內的元素是有序的,元素可以重復;后者元素無序,但元素不可重復。那么這里就有一個比較嚴重的問題了:要想保證元素不重復,可兩個元素是否重復應該依據什么來判斷呢?這就是 Object.equals方法了。但是,如果每增加一個元素就檢查一次,那么當元素很多時,后添加到集合中的元素比較的次數就非常多了。也就是說,如果集合中現在已經有1000個元素,那么第1001個元素加入集合時,它就要調用1000次equals方法。這顯然會大大降低效率。

 

於是,Java采用了哈希表的原理。哈希算法也稱為散列算法,是將數據依特定算法直接指定到一個地址上。關於哈希算法,這里就不詳細介紹。可以這樣簡單理解,hashCode方法實際上返回的就是對象存儲位置的映像。

這樣一來,當集合要添加新的元素時,先調用這個元素的hashCode方法,就能定位到它應該放置的存儲位置。如果這個位置上沒有元素,它就可以直接存儲在這個位置上,不用再進行任何比較了;如果這個位置上已經有元素了,就調用它的equals方法與新元素進行比較,相同的話就不存了,不相同就表示發生沖突了,散列表對於沖突有具體的解決辦法,但最終還會將新元素保存在適當的位置。這樣一來,實際調用equals方法的次數就大大降低了,幾乎只需要一兩次。

原則:

在編寫類的時候,如果覆蓋了Object的equals方法,那么必須要覆蓋hashCode方法,並且如果兩個對象用equals方法比較返回true,那么這兩個對象hashCode返回的值也必須是相等的,並且對於同一個對象,equals方法需要比較的屬性值沒有被修改,那么每次調用hashCode返回的值應該是一致的。

重寫:

Google首席Java架構師Joshua Bloch在他的著作《Effective Java》中提出了一種簡單通用的hashCode算法
1. 初始化一個整形變量,為此變量賦予一個非零的常數值,比如int result = 17;
2. 選取equals方法中用於比較的所有域,然后針對每個域的屬性進行計算:
  (1) 如果是boolean值,則計算f ? 1:0
  (2) 如果是byte\char\short\int,則計算(int)f
  (3) 如果是long值,則計算(int)(f ^ (f >>> 32))
  (4) 如果是float值,則計算Float.floatToIntBits(f)
  (5) 如果是double值,則計算Double.doubleToLongBits(f),然后返回的結果是long,再用規則(3)去處理long,得到int
  (6) 如果是對象應用,如果equals方法中采取遞歸調用的比較方式,那么hashCode中同樣采取遞歸調用hashCode的方式。  否則需要為這個域計算一個范式,比如當這個域的值為null的時候,那么hashCode 值為0
  (7) 如果是數組,那么需要為每個元素當做單獨的域來處理。如果你使用的是1.5及以上版本的JDK,那么沒必要自己去    重新遍歷一遍數組,java.util.Arrays.hashCode方法包含了8種基本類型數組和引用數組的hashCode計算,算法同上,
  java.util.Arrays.hashCode(long[])的具體實現:
 
1
2
3
4
5
6
7
8
9
10
11
12
public static int hashCode( long a[]) {
         if (a == null )
             return 0 ;
 
         int result = 1 ;
         for ( long element : a) {
             int elementHash = ( int )(element ^ (element >>> 32 ));
             result = 31 * result + elementHash;
         }
 
         return result;
}

 

Arrays.hashCode(...)只會計算一維數組元素的hashCOde,如果是多維數組,那么需要遞歸進行hashCode的計算,那么就需要使用Arrays.deepHashCode(Object[])方法。
 
3. 最后,要如同上面的代碼,把每個域的散列碼合並到result當中:result = 31 * result + elementHash;
4. 測試,hashCode方法是否符合文章開頭說的基本原則,這些基本原則雖然不能保證性能,但是可以保證不出錯。
 

例子:

String.hashCode();

public int hashCode() {
        int hash = hashCode;
        if (hash == 0) {
            if (count == 0) {
                return 0;
            }
            final int end = count + offset;
            final char[] chars = value;
            for (int i = offset; i < end; ++i) {
                hash = 31*hash + chars[i];
            }
            hashCode = hash;
        }
        return hash;
    }

問題:

hashcode 通過什么散列算法實現的。

怎么解釋以上的hash算法,為什么要*31,為什么要》》》31 等。

部分參考資料:

http://my.oschina.net/chihz/blog/56256


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM