什么是哈希表?
哈希表(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());
}
}