文章目录
一、哈希算法
1、哈希表概述
(1)哈希表介绍
- 哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
- 哈希表hashtable(key,value) 就是把Key通过一个固定的算法函数既所谓的哈希函数转换成一个整型数字,然后就将该数字对数组长度进行取余,取余结果就当作数组的下标,将value存储在以该数字为下标的数组空间里。而当使用哈希表进行查询的时候,就是再次使用哈希函数将key转换为对应的数组下标,并定位到该空间获取value,如此一来,就可以充分利用到数组的定位性能进行数据定位。
(2)哈希表结构
图中左边很明显是个数组,数组的每个成员包括一个指针,指向一个链表的头,当然这个链表可能为空,也可能元素很多。我们根据元素的一些特征把元素分配到不同的链表中去,也是根据这些特征,找到正确的链表,再从链表中找出这个元素。
2、哈希算法示例
(1)创建一个Student类
/*Student类,代表一个Student*/
public class Student {
public int id;
public String name;
/*指向下一个节点的引用,默认为空*/
public Student nextStudent;
public Student(int id, String name) {
this.id = id;
this.name = name;
}
}
(2)创建SchoolLinkedList类,代表一条链表,链表中存放Student对象
/*SchoolLinkedList类,表示链表*/
public class SchoolLinkedList {
/*头指针,执行第一个Student,所以直接指向Student*/
private Student headStudent;
/*添加student到School:假定id时自增长,
所以添加的时候会添加到链表的末尾*/
public void addStudent(Student student){
/*如果要添加的是第一个student*/
if(headStudent == null){
headStudent = student;
return;
}
/*如果要添加的不是第一个Student,则定位到链表的末尾,
将student添加到末尾位置*/
Student tempStudent = headStudent;
while (true){
/*判断链表是否到最后*/
if(tempStudent.nextStudent == null){
break;
}
/*没有到链表的最后就继续向后定位*/
tempStudent = tempStudent.nextStudent;
}
/*将student添加到链表最后*/
tempStudent.nextStudent = student;
}
/* 遍历链表中的信息*/
public void show(int num){
if(headStudent == null ){
System.out.println("第"+ num +"链表的信息为空");
return;
}
System.out.println("第"+ num +"链表中的信息为:");
Student student = headStudent;
while(true){
System.out.println(" id = "+ student.id +" name = " + student.name);
if(student.nextStudent == null){
break;
}
student = student.nextStudent;
}
}
/*查找student*/
public Student search(int id){
if(headStudent == null){
return null;
}
Student tempStudent = headStudent;
while(true){
/*链表为空直接返回null*/
if(tempStudent.id == id){
break;
}
/*没有查找到就退出*/
if(tempStudent.nextStudent == null){
tempStudent = null;
break;
}
tempStudent = tempStudent.nextStudent;
}
return tempStudent;
}
}
(3)创建哈希表,用于管理多条链表
/*哈希表,用于管理多条链表*/
public class HashTable {
private SchoolLinkedList[] schoolLinkedListArray;
/*size表示共有多少链表*/
private int size;
/*构造器*/
public HashTable(int size) {
this.size = size;
/*初始化*/
schoolLinkedListArray = new SchoolLinkedList[size];
/*初始化每一个链表*/
for (int i = 0; i < size; i++){
schoolLinkedListArray[i] = new SchoolLinkedList();
}
}
/*添加student*/
public void add(Student student){
/*根据student的id判断该student应该添加到那个链表*/
int schoolLinkedListNum = (student.id) % size;
/*添加到对应链表*/
schoolLinkedListArray[schoolLinkedListNum].addStudent(student);
}
/*遍历所有的链表即哈希表*/
public void showHashTable(){
for (int i = 0; i < size; i++) {
schoolLinkedListArray[i].show(i);
}
}
/*查找student*/
public void searchById(int id){
int schoolLinkedListNum = id % size;
Student student = schoolLinkedListArray[schoolLinkedListNum].search(id);
if(student != null){
System.out.println("在第" + schoolLinkedListNum + "条链表中找到id为" + id + "的student");
}else{
System.out.println("没有找到该student");
}
}
}
(4)测试类
public class HashTableTest {
public static void main(String[] args) {
/*创建哈希表*/
HashTable hashTable = new HashTable(7);
/**/
int num ;
Scanner scanner = new Scanner(System.in);
while(true){
System.out.println("\n请选择相应的操作: 1:添加student 2:显示student 3:查找student 4:退出系统");
num = scanner.nextInt();
switch (num){
case 1:
/*输入要添加的student信息*/
System.out.print("请输入id:");
int id = scanner.nextInt();
System.out.print("请输入名字:");
String name = scanner.next();
/*添加student*/
Student student = new Student(id, name);
hashTable.add(student);
break;
case 2:
hashTable.showHashTable();
break;
case 3:
System.out.println("请输入要查找的student的id:");
id = scanner.nextInt();
hashTable.searchById(id);
break;
case 4:
scanner.close();
System.out.println("系统退出");
System.exit(0);
break;
default:
break;
}
}
}
}
(5)测试结果
请选择相应的操作: 1:添加student 2:显示student 3:查找student 4:退出系统
1
请输入id:0
请输入名字:张三
请选择相应的操作: 1:添加student 2:显示student 3:查找student 4:退出系统
1
请输入id:5
请输入名字:李四
请选择相应的操作: 1:添加student 2:显示student 3:查找student 4:退出系统
1
请输入id:12
请输入名字:王五
请选择相应的操作: 1:添加student 2:显示student 3:查找student 4:退出系统
2
第0链表中的信息为:
id = 0 name = 张三
第1链表的信息为空
第2链表的信息为空
第3链表的信息为空
第4链表的信息为空
第5链表中的信息为:
id = 5 name = 李四
id = 12 name = 王五
第6链表的信息为空
请选择相应的操作: 1:添加student 2:显示student 3:查找student 4:退出系统
3
请输入要查找的student的id:
5
在第5条链表中找到id为5的student
请选择相应的操作: 1:添加student 2:显示student 3:查找student 4:退出系统
3
请输入要查找的student的id:
10
没有找到该student
请选择相应的操作: 1:添加student 2:显示student 3:查找student 4:退出系统
4
系统退出
3、哈希算法的应用场景
(1)全加密
日常用户密码加密通常使用的都是 md5、sha等哈希函数,因为不可逆,而且微小的区别加密之后的结果差距很大,所以安全性更好。
(2)唯一标识
比如 URL 字段或者图片字段要求不能重复,这个时候就可以通过对相应字段值做 md5 处理,将数据统一为 32 位长度从数据库索引构建和查询角度效果更好,此外,还可以对文件之类的二进制数据做 md5 处理,作为唯一标识,这样判定重复文件的时候更快捷。
(3)数据校验
比如从网上下载的很多文件(尤其是P2P站点资源),都会包含一个 MD5 值,用于校验下载数据的完整性,避免数据在中途被劫持篡改。
(4)散列函数
前面已经提到,PHP 中的 md5、sha1、hash 等函数都是基于哈希算法计算散列值
(5)负载均衡
对于同一个客户端上的请求,尤其是已登录用户的请求,需要将其会话请求都路由到同一台机器,以保证数据的一致性,这可以借助哈希算法来实现,通过用户 ID 尾号对总机器数取模(取多少位可以根据机器数定),将结果值作为机器编号。
(6)分布式缓存
分布式缓存和其他机器或数据库的分布式不一样,因为每台机器存放的缓存数据不一致,每当缓存机器扩容时,需要对缓存存放机器进行重新索引(或者部分重新索引),这里应用到的也是哈希算法的思想。