除數為2的N次方取模可以用與運算替代,效率更高
阿新 • • 發佈:2019-02-07
取模運算在包括JAVA在內的大多數語言中的效率都十分低下,而當除數為2的N次方時,取模運算將退化為最簡單的位運算,其效率明顯提升(按照Bruce Eckel給出的資料,大約可以提升5~8倍) 。看看JDK中是如何實現的:
Java程式碼:
當key空間長度為2的N次方時,計算hashCode為h的元素的索引可以用簡單的與操作來代替笨拙的取模操作!假設某個物件的hashCode為35(二進位制為100011),而hashMap採用預設的initialCapacity(16),那麼indexFor計算所得結果將會是100011 & 1111 = 11,即十進位制的3,是不是恰好是35 Mod 16。
上面的方法有一個問題,就是它的計算結果僅有物件hashCode的低位決定,而高位被統統遮蔽了;以上面為例,19(10011)、35(100011)、67(1000011)等就具有相同的結果。針對這個問題, Joshua Bloch採用了“防禦性程式設計”的解決方法,在使用各物件的hashCode之前對其進行二次Hash,參看JDK中的原始碼:
採用這種旋轉Hash函式的主要目的是讓原有hashCode的高位資訊也能被充分利用,且兼顧計算效率以及資料統計的特性,其具體的原理已超出了本文的領域。
加快Hash效率的另一個有效途徑是編寫良好的自定義物件的HashCode,String的實現採用瞭如下的計算方法:
這種方法HashCode的計算方法可能最早出現在Brian W. Kernighan和Dennis M. Ritchie的《The C Programming Language》中,被認為是價效比最高的演算法(又被稱為times33演算法,因為C中乘數常量為33,JAVA中改為31),實際上,包括List在內的大多數的物件都是用這種方法計算Hash值。
Java程式碼:
- staticint indexFor(int h, int length) {
- return h & (length-1);
- }
- staticint indexFor(int h, int length) {
- return h & (length-1);
- }
當key空間長度為2的N次方時,計算hashCode為h的元素的索引可以用簡單的與操作來代替笨拙的取模操作!假設某個物件的hashCode為35(二進位制為100011),而hashMap採用預設的initialCapacity(16),那麼indexFor計算所得結果將會是100011 & 1111 = 11,即十進位制的3,是不是恰好是35 Mod 16。
上面的方法有一個問題,就是它的計算結果僅有物件hashCode的低位決定,而高位被統統遮蔽了;以上面為例,19(10011)、35(100011)、67(1000011)等就具有相同的結果。針對這個問題, Joshua Bloch採用了“防禦性程式設計”的解決方法,在使用各物件的hashCode之前對其進行二次Hash,參看JDK中的原始碼:
- staticint hash(Object x) {
- int h = x.hashCode();
- h += ~(h << 9);
- h ^= (h >>> 14);
-
h += (h <<
- h ^= (h >>> 10);
- return h;
- }
- staticint hash(Object x) {
- int h = x.hashCode();
- h += ~(h << 9);
- h ^= (h >>> 14);
- h += (h << 4);
- h ^= (h >>> 10);
- return h;
- }
採用這種旋轉Hash函式的主要目的是讓原有hashCode的高位資訊也能被充分利用,且兼顧計算效率以及資料統計的特性,其具體的原理已超出了本文的領域。
加快Hash效率的另一個有效途徑是編寫良好的自定義物件的HashCode,String的實現採用瞭如下的計算方法:
- for (int i = 0; i < len; i++) {
- h = 31*h + val[off++];
- }
- hash = h;
- for (int i = 0; i < len; i++) {
- h = 31*h + val[off++];
- }
- hash = h;
這種方法HashCode的計算方法可能最早出現在Brian W. Kernighan和Dennis M. Ritchie的《The C Programming Language》中,被認為是價效比最高的演算法(又被稱為times33演算法,因為C中乘數常量為33,JAVA中改為31),實際上,包括List在內的大多數的物件都是用這種方法計算Hash值。