標籤:cti 分布 body 線性 result log equals import array
散列的價值在於速度。我們使用數組來儲存鍵的資訊,這個資訊並不是鍵本身,而是通過鍵對象產生一個數字(散列碼),作為數組下標。由於數組的容量是固定的,而散列容器的大小是可變的,所以不同的鍵可以產生相同的數組下標(散列碼)。也就是說,可能會有衝突(當然也有特例,比如EnumMap和EnumSet)。所以,數組的值存放著一個儲存所有相同散列碼的值的list(引用)。然後對list中的值使用equals進行線性查詢。如果散列函數設計的比較好的話,數組的每個位置只有較少的值,並且浪費空間也小。於是,查詢過程就是首先計算鍵的散列碼得到數組下標,然後記憶體定址(時間複雜度為O(1),賦值)找到數組的值,再遍曆list(時間複雜度為O(n),線性查詢)即可。即hashCode和equals共同確定了對象的唯一性。
所有類都繼承於Object。Object的hashCode方法產生的散列碼,實際上是預設使用對象的地址計算散列碼;而Object的equals方法實際上就是地址比較(==)。顯然,當我們在使用散列容器(如HashMap的Key,HashSet等)時,我們自訂的類中還繼承Object的hashCode和equals是不行的。必須重寫hashCode和equals方法。好的hashCode()應該產生分布均勻的散列碼。可以用IDE自動產生。下面是一個例子:
1 import java.util.List; 2 3 public class Test9 { 4 5 boolean a; 6 byte b; 7 short c; 8 int d; 9 char e;10 long f;11 float g;12 double h;13 String i;14 List<String> j;15 int[] k;16 17 @Override18 public int hashCode() {19 // [STEP1] hashCode()裡的魔法數字,之所以選擇31,是因為它是個奇素數,如果乘數是偶數,並且乘法溢出的話,資訊就會丟失,因為與2相乘等價於移位元運算。20 // 使用素數的好處並不是很明顯,但是習慣上都使用素數來計算散列結果。31有個很好的特性,就是用移位和減法來代替乘法,可以得到更好的效能:31*i==(i<<5)-i。現在的VM可以自動完成這種最佳化。(from 《Effective Java》)21 final int prime = 31;22 // [STEP2] 為對象中每個有意義的域用下面公式計算散列碼 result = prime * result + c23 int result = 1;24 // boolean25 result = prime * result + (a ? 1231 : 1237);26 // byte/short/int/char27 result = prime * result + b;28 result = prime * result + c;29 result = prime * result + d;30 result = prime * result + e;31 // long32 result = prime * result + (int) (f ^ (f >>> 32));33 // float34 result = prime * result + Float.floatToIntBits(g);35 // double36 long temp;37 temp = Double.doubleToLongBits(h);38 result = prime * result + (int) (temp ^ (temp >>> 32));39 // 對象40 result = prime * result + ((i == null) ? 0 : i.hashCode());41 // List(要求每個元素實現hashCode)42 result = prime * result + ((j == null) ? 0 : j.hashCode());43 // 數組(要求每個元素實現hashCode)44 result = prime * result + Arrays.hashCode(k);45 // [STEP3] 返回46 return result;47 }48 @Override49 public boolean equals(Object obj) {50 if (this == obj)51 return true;52 if (obj == null)53 return false;54 if (getClass() != obj.getClass())55 return false;56 Test9 other = (Test9) obj;57 if (a != other.a)58 return false;59 if (b != other.b)60 return false;61 if (c != other.c)62 return false;63 if (d != other.d)64 return false;65 if (e != other.e)66 return false;67 if (f != other.f)68 return false;69 if (Float.floatToIntBits(g) != Float.floatToIntBits(other.g))70 return false;71 if (Double.doubleToLongBits(h) != Double.doubleToLongBits(other.h))72 return false;73 if (i == null) {74 if (other.i != null)75 return false;76 } else if (!i.equals(other.i))77 return false;78 if (j == null) {79 if (other.j != null)80 return false;81 } else if (!j.equals(other.j))82 return false;83 if (!Arrays.equals(k, other.k))84 return false;85 return true;86 }87 }
Java集合(7):散列與散列碼