哈希表的实现(代码实现)
这里我们使用哈希表解决我们前面讲哈希表的介绍和内存布局的时候讲过的Google上机题(如下: )
哈希表实际使用需求 — Google上机题:
有一个实际需求 : 有一个公式,当有新的员工来报到时,要去将该员工的信息加入(信息有id,年龄, 性别 , 住址…) , 当输入该员工的id的时候要去可以查到该员工的所有信息
题目要求: 不使用数据库, 尽量节省内存 ,速度越快越好
具体代码如下:
package hashtab;
import java.awt.*;
//创建我们的结点类 (一个节点就表示一个员工)
class Emp{
public int id;
public String name;
public Emp next;
public Emp(int id, String name){
this.id = id;
this.name = name;
}
}
//创建EmpLinkedList类表示链表
class EmpLinkedList{
//头指针 ,这个时候没有头结点 ,所以我们的头指针是是指向我们的首元节点的 --> 我们其实这个时候可以将其称之为首元指针
private Emp head; //默认为null
//添加雇员到链表
//这个时候我们假设添加雇员的时候雇员的id属性值是递增的, 也就是这个时候我们添加的时候每次就只需要加入到本链表的最后一个位置即可
public void add(Emp emp){
//如果这个时候是添加第一个雇员:
//这个时候有一些特殊性 ,我们就要进行特殊处理 , 也就是放到对应的if判断中执行
if (head == null){
head = emp; //使用传入的第一个雇员作为链表的首元节点
return;
}
//如果不是第一个雇员, 则使用一个辅助指针帮助定位链表中的最后一个节点的位置 ,因为我们添加结点的时候对于单向链表来讲肯定要使用前一个节点来完成对链表表元素的添加操作
Emp curEmp = head;
while(true){
if(curEmp.next == null){//说明已经遍历到了链表的最后面的位置, 并且这个时候我们的curEmp指针是指向了我们的链表中的最后一个节点的位置
break;
}
curEmp = curEmp.next; //后移
}
//将元素加入到链表中(此时是直接加入到队尾就可以了)
curEmp.next = emp;
}
//编写一个方法 , 根据id查找雇员
//如果找到就返回对应的EMP结点, 如果没有找到就返回一个null即可
public Emp findEmpById(int id){
//判断头结点是否为空
if(head == null){
System.out.println("链表为空~~");
return null;
}
//创建一个辅助指针 , 开始遍历(从头指针指向的位置开始(因为这个时候我们的头指针指向的是我们的首元节点 ,而不是头结点 , 此时我们没有头结点))
Emp curEmp = head;
while(true){
if(curEmp.id == id){
//此时就是表示已经找到了 ,此时我们的curEmp指向了我们的待返回的结点
break;
}
//那么如果已经遍历到链表的最后也没有找到此时我们就要让我们的curEmp指向一个null , 我们最后统一的返回这个curEmp指针即可
if(curEmp.next == null){
curEmp = null;
}
//指针后移
curEmp = curEmp.next;
}
return curEmp;
}
//编写一个方法 用来遍历链表
//遍历打印链表中的雇员信息
/**
* @param n 用来表示是第几条链表(因为此时是一个哈希表 , 我们具体是第几条链表并不是固定的)
*/
public void list(int n) {
//我们要遍历之前先判断一下我们的头结点是否为空 , 如果为空那么我们就不用遍历 , 如果为空, 我们依旧遍历就有可能出现一个空指针异常
if (head == null) {
System.out.println("第" + (n + 1) +"条链表为空~~");
return;
}
System.out.print("第" + (n + 1) + "条链表的信息 : ");
//定义一个临时变量用来遍历链表
Emp curEmp = head;
while (true) {
System.out.printf("=> id = %d name = %s\t", curEmp.id, curEmp.name);
if (curEmp.next == null) {
break;
}
//后移操作
curEmp = curEmp.next;
}
System.out.println();
}
}
/**
* 定义我们的哈希表类 (创建一个HashTab类来管理多条链表)
*/
public class HashTab {
private EmpLinkedList[] empLinkedListArray; //哈希表底层结构
private int size; //底层数组长度 , 我们要利用这个数组长度来计算哈希值(使用取模法)
public HashTab(int size){
//初始化哈希表
this.empLinkedListArray = new EmpLinkedList[size];
this.size = size;
/*
初始化链表 , 此时我们只是初始化了一个链表类型的数组 ,但是这个数组中都是默认值 , 也就是都是null , 并不是链表
*/
for (int i = 0; i < size; i++) {
empLinkedListArray[i] = new EmpLinkedList();
}
}
//编写一个计算结点哈希值的方法 ---> 这里我们是通过id属性来计算
public int hashFun(int id){
return id % size; //这里我们使用取模法来计算结点的hash值
}
//编写一个方法用于向哈希表中添加结点
public void add(Emp emp){
//计算结点的hash值
int hash = hashFun(emp.id);
//hash值其实就是我们要添加到的链表 ,此时我们只要将此结点添加到对应的链表上即可
empLinkedListArray[hash].add(emp);
}
//编写一个查找雇员结点的方法 : 根据id属性值进行一个查找
public void findEmpByID(int id){
//使用散列函数计算对应id的hash值
int empLinkedListNo = hashFun(id);
Emp emp = empLinkedListArray[empLinkedListNo].findEmpById(id);
//判断是否找到 :
if(emp != null){
System.out.printf("在第%d条链表中找到了雇员id = %d\n", empLinkedListNo + 1, id);
}else{
System.out.println("在哈希表中没有找到该雇员~~");
}
}
//编写一个遍历HashTab(哈希表)中所有雇员信息的方法
public void list(){
for (int i = 0; i < size; i++) {
empLinkedListArray[i].list(i);
}
}
//编写测试代码:
public static void main(String[] args) {
HashTab hashTab = new HashTab(10);
Emp emp = new Emp(1,"马云");
Emp emp1 = new Emp(2,"马化腾");
Emp emp2 = new Emp(3,"雷军");
hashTab.add(emp);
hashTab.list();
System.out.println();
hashTab.add(emp1);
hashTab.list();
System.out.println();
hashTab.add(emp2);
hashTab.list();
System.out.println();
//测试查找:
hashTab.findEmpByID(2);
}
}
- 这段代码中一共有三个类 , 一个节点类 , 一个是底层链表类 , 一个是哈希表类 , 我们在链表类中也封装了很多的对应的操作链表的方法, 因为最终我们都是讲元素存储到了底层的链表中 ,然后将链表存储到了底层数组中 , 但是我们要知道最终我们要使用HashTab类的对象(也就是Hash表)来进行对应的操作 ,所以我们最终要在HashTab类中创建对应的方法 , 我们在HashTab类中的对应方法中直接调用我们的链表类中的方法即可