什麼是哈希表?
哈希表(Hash table,也叫散列表),是根據關鍵碼值(Key value)而直接進行訪問的數據結構。也就是說,它通過把關鍵碼值映射到表中一個位置來訪問記錄,以加快查找的速度。這個映射函數叫做哈希函數,存放記錄的數組叫做哈希表。
哈希表充分體現了算法設計領域的經典思想:用空間換時間。
哈希表是時間與空間之間的平衡。
哈希函數的設計也是很重要的。
“鍵”通過哈希函數得倒的“索引”分佈越均勻越好。
我們關注的“鍵"的類型,在轉化爲數組的索引時一般哈希函數的設計原則:
整型
小範圍正整數直接使用
小範圍負整數進行偏移( -100~100—>0~200)
大整數的通常做法:取模,取模時要具體問題具體分析。一個簡單的解決辦法就是模一個素數(素數又稱質數。一個大於1的自然數,除了1和它自身外,不能被其他自然數整除的數叫做質數;否則稱爲合數)。一個很小的例子來說明爲什麼要模一個素數,而不是合數:
取餘合數4:
10%4 | 2 |
---|---|
20%4 | 0 |
30%4 | 2 |
40%4 | 0 |
50%4 | 2 |
取餘素數7:
10%7 | 3 |
---|---|
20%7 | 6 |
30%7 | 2 |
40%7 | 5 |
50%7 | 1 |
取模素數時”索引“更連續,哈希衝突更少。
浮點型:
在計算機中,浮點型都是32位(float)或者64位(double)的二進制表示,被計算機解析成了浮點數。所以浮點數可以轉成整型來處理。
字符串:
轉成整型處理
166 = 1 * 10 ^ 2 + 6 * 10 ^ 1 + 6 * 10 ^ 0
code = c * B ^ 3 + o * B ^ 2 + d * B ^ 1 + e * B ^ 0
hash(code) = (c * B ^ 3 + o * B ^ 2 + d * B ^ 1 + e * B ^ 0 ) % M
hash(code) = ((((c * B) + o) * B + d) * B + e ) % M
hash(code) = ((((c % M) * B + o) % M * B + d) % M * B + e ) % M
最後一個式子轉換爲代碼:
int hash = 0;
for(int i = 0; i < str.length(); i ++){
hash = (hash * B + str.charAt(i)) % M
}
複合類型
依然可以轉爲整型類型。即像處理字符串一樣把複合類型的每一部分分開來處理。
當然,轉成整型處理,並不是唯一的方法,只是一般的通用方法。
但不管怎樣,在設計哈希函數時我們要遵循下面三個原則:
- 一致性:即a==b,則hash(a) == hash(b)
- 高效性:計算高效簡便
- 均勻性:哈希值均勻分佈
定義一個Student並重寫它的hashCode與equals方法:
/**
* @author ymn
* @version 1.0
* @date 2020\6\5 0005 16:00
*/
public class Student {
int grade;
int cls;
String firstName;
String lastName;
public Student(int grade, int cls, String firstName, String lastName) {
this.grade = grade;
this.cls = cls;
this.firstName = firstName;
this.lastName = lastName;
}
@Override
public int hashCode() {
int B = 7;
int hash =0;
hash = hash * B + grade;
hash = hash * B + cls;
hash = hash * B + firstName.toLowerCase().hashCode();
hash = hash * B + lastName.toLowerCase().hashCode();
return hash;
}
@Override
public boolean equals(Object obj) {
//判斷當前對象與obj的引用是否相同
if (this == obj){
return true;
}
//o是否爲空
if (obj == null){
return false;
}
//是否是一個類,避免出現傳進來的類是Student子類的情況
if (getClass() != obj.getClass()){
return false;
}
Student another = (Student)obj;
return this.cls == another.cls &&
this.grade == another.grade &&
this.firstName.toLowerCase().equals(another.firstName.toLowerCase()) &&
this.lastName.toLowerCase().equals(another.lastName.toLowerCase());
}
}