一、哈希表的基本介绍
java程序要操作数据,应该是这样的
java程序操作数据库,数据库中再进行一系列操作,最后将结果返回给java程序
但是这种结构会对数据库的操作比较频繁,有一些数据没有必要每次都查询数据库,一般处理方法是把常用的数据添加到缓存层,需要时在缓存层提取数据
如果使用缓存产品做缓存太过于奢侈或者没有缓存产品,可以自己写哈希表当缓存层
如果一级缓存层不够甚至可以做二级缓存层:
二、哈希表例题
package hash;
public class HashTable {
}
//表示一个雇员
class Emp {
public int id;
public String name;
public Emp next;// next默认为null
public Emp(int id, String name) {
super();
this.id = id;
this.name = name;
}
}
//创建EmpLinkedList,表示链表
class EmpLinkedList {
// 头指针,执行第一个Emp,因此我们这个链表的head是直接指向第一个雇员的
private Emp head;// 默认null
// 添加雇员到链表
// 假定添加雇员的时候就是id是自增长的,即id的分配总是从小到大
// 因此我们将该雇员直接加人到本链表的最后即可
public void add(Emp emp) {
// 如果是添加第一个雇员
if (head == null) {
head = emp;
return;
}
// 如果不是第一个雇员。则使用一个辅助的指针,帮助定位到最后
Emp curEmp = head;
while (true) {
if (curEmp.next == null) {
break;
}
curEmp = curEmp.next;// 后移
}
// 退出时直接将emp加入链表
curEmp.next = emp;
}
// 遍历链表的雇员信息
public void list() {
if (head == null) {
System.out.println("当前链表的信息为空");
return;// 直接结束list方法
}
System.out.println("当前链表的信息为");
Emp curEmp = head;// 辅助指针
while (true) {
System.out.printf("=> id=id%d name=%s\t", curEmp.id, curEmp.name);
if (curEmp.next == null) {// 说明curEmp已经是最后结点
break;
}
curEmp = curEmp.next;// 后移,遍历
}
System.out.println();
}
}
//哈希表之所以能够提高效率是因为他可以同时管理多条链表,可以根据散列函数决定这个雇员应该加到哪一条链表中去
class HashTab {
private EmpLinkedList[] empLinkedListArrays;
// 构造器
public HashTab(int size) {
// 初始化empLinkedListArrays
empLinkedListArrays = new EmpLinkedList[size];
// 添加雇员
}
}
真正的添加的行为是找HashTable,而不是找EmpLinkedList,因为对外层来看
我们只看到的是这个蓝色的框框,至于里边找哪条链表去添加是 HashTable 来完成的,实际上是把他包起来的。
package hash;
import java.util.Scanner;
public class HashTable {
public static void main(String[] args) {
HashTab hashTab = new HashTab(7);
// 写一个简单的菜单
String key = "";
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("add:添加雇员");
System.out.println("list:显示雇员");
System.out.println("exit:退出系统");
key = scanner.next();
switch (key) {
case "add":
System.out.println("输入id");
int id = scanner.nextInt();
System.out.println("输入名字");
String name = scanner.next();
// 创建雇员
Emp emp = new Emp(id, name);
hashTab.add(emp);
break;
case "list":
hashTab.list();
break;
case "exit":
scanner.close();
System.exit(0);
default:
break;
}
}
}
}
//表示一个雇员
class Emp {
public int id;
public String name;
public Emp next;// next默认为null
public Emp(int id, String name) {
super();
this.id = id;
this.name = name;
}
}
//创建EmpLinkedList,表示链表
class EmpLinkedList {
// 头指针,执行第一个Emp,因此我们这个链表的head是直接指向第一个雇员的
private Emp head;// 默认null
// 添加雇员到链表
// 假定添加雇员的时候就是id是自增长的,即id的分配总是从小到大
// 因此我们将该雇员直接加人到本链表的最后即可
public void add(Emp emp) {
// 如果是添加第一个雇员
if (head == null) {
head = emp;
return;
}
// 如果不是第一个雇员。则使用一个辅助的指针,帮助定位到最后
Emp curEmp = head;
while (true) {
if (curEmp.next == null) {
break;
}
curEmp = curEmp.next;// 后移
}
// 退出时直接将emp加入链表
curEmp.next = emp;
}
// 遍历链表的雇员信息
public void list() {
if (head == null) {
System.out.println("当前链表的信息为空");
return;// 直接结束list方法
}
System.out.println("当前链表的信息为");
Emp curEmp = head;// 辅助指针
while (true) {
System.out.printf("=> id=id%d name=%s\t", curEmp.id, curEmp.name);
if (curEmp.next == null) {// 说明curEmp已经是最后结点
break;
}
curEmp = curEmp.next;// 后移,遍历
}
System.out.println();
}
}
//哈希表之所以能够提高效率是因为他可以同时管理多条链表,可以根据散列函数决定这个雇员应该加到哪一条链表中去
class HashTab {
private EmpLinkedList[] empLinkedListArrays;
private int size;// 表示共有多少条链表
// 构造器
public HashTab(int size) {
this.size = size;
// 初始化empLinkedListArrays
empLinkedListArrays = new EmpLinkedList[size];
}
// 添加雇员
public void add(Emp emp) {
// 根据员工的id得到该员工应当加入到哪条链表
int empLinkedListNo = hashFun(emp.id);
// 将emp添加到对应链表中
empLinkedListArrays[empLinkedListNo].add(emp);
}
// 遍历所有的链表,遍历哈希表
public void list() {
for (int i = 0; i < size; i++) {
empLinkedListArrays[i].list();
}
}
// 编写一个散列函数,使用取模法
public int hashFun(int id) {
return id % size;
}
}
代码测试之后
add:添加雇员
list:显示雇员
exit:退出系统
add
输入id
1
输入名字
shi
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "hash.EmpLinkedList.add(hash.Emp)" because "this.empLinkedListArrays[empLinkedListNo]" is null
at hash.HashTab.add(HashTable.java:125)
at hash.HashTable.main(HashTable.java:30)
出现了空指针错误:
这是因为
empLinkedListArrays[empLinkedListNo].add(emp);
empLinkedListArrays[empLinkedListNo]
这一部分是null
因为在创建这个链表的时候仅仅是把数组本身创建起来了
也就是说这个蓝色的框确实已经创建起来了,但是里边的
为空,不能把EmlLinkedList直接放在空(空指针和空链表是不一样的,链表里边有属性等等,直接创建数组一般里边默认都是0 ,所以不能直接放进去)
这时候不要忘了分别初始化每一条链表
也就是说数组里的每一个元素都要创建一下
完整代码演示:
package hash;
import java.util.Scanner;
public class HashTable {
public static void main(String[] args) {
HashTab hashTab = new HashTab(7);
// 写一个简单的菜单
String key = "";
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("add:添加雇员");
System.out.println("list:显示雇员");
System.out.println("exit:退出系统");
System.out.println("find:查找雇员");
key = scanner.next();
switch (key) {
case "add":
System.out.println("输入id");
int id = scanner.nextInt();
System.out.println("输入名字");
String name = scanner.next();
// 创建雇员
Emp emp = new Emp(id, name);
hashTab.add(emp);
break;
case "list":
hashTab.list();
break;
case "find":
System.out.println("输入要查找雇员的id");
id = scanner.nextInt();
hashTab.findEmpById(id);
break;
case "exit":
scanner.close();
System.exit(0);
default:
break;
}
}
}
}
//表示一个雇员
class Emp {
public int id;
public String name;
public Emp next;// next默认为null
public Emp(int id, String name) {
super();
this.id = id;
this.name = name;
}
}
//创建EmpLinkedList,表示链表
class EmpLinkedList {
// 头指针,执行第一个Emp,因此我们这个链表的head是直接指向第一个雇员的
private Emp head;// 默认null
// 添加雇员到链表
// 假定添加雇员的时候就是id是自增长的,即id的分配总是从小到大
// 因此我们将该雇员直接加人到本链表的最后即可
public void add(Emp emp) {
// 如果是添加第一个雇员
if (head == null) {
head = emp;
return;
}
// 如果不是第一个雇员。则使用一个辅助的指针,帮助定位到最后
Emp curEmp = head;
while (true) {
if (curEmp.next == null) {
break;
}
curEmp = curEmp.next;// 后移
}
// 退出时直接将emp加入链表
curEmp.next = emp;
}
// 遍历链表的雇员信息
public void list(int no) {
if (head == null) {
System.out.println("第"+(no+1)+"链表的信息为空");
return;// 直接结束list方法
}
System.out.println("当前链表的信息为");
Emp curEmp = head;// 辅助指针
while (true) {
System.out.printf("=> id=%d name=%s\t", curEmp.id, curEmp.name);
if (curEmp.next == null) {// 说明curEmp已经是最后结点
break;
}
curEmp = curEmp.next;// 后移,遍历
}
System.out.println();
}
//根据id查找雇员
//如果查找到就返回Emp,如果没有找到就返回空
public Emp findEmpById(int id) {
if(head==null) {
System.out.println("链表为空");
return null;
}
Emp curEmp=head;
while(true) {
if(curEmp.id==id) {//找到
break;//这时curEmp就指向要查找的雇员
}
//退出
if(curEmp.next==null) {//说明遍历当前链表没有找到该雇员
curEmp=null;
break;
}
}
return curEmp;
}
}
//哈希表之所以能够提高效率是因为他可以同时管理多条链表,可以根据散列函数决定这个雇员应该加到哪一条链表中去
class HashTab {
private EmpLinkedList[] empLinkedListArrays;
private int size;// 表示共有多少条链表
// 构造器
public HashTab(int size) {
this.size = size;
// 初始化empLinkedListArrays
empLinkedListArrays = new EmpLinkedList[size];
//这时要分别初始化每一个链表
for(int i=0;i<size;i++) {
empLinkedListArrays[i]=new EmpLinkedList();
}
}
// 添加雇员
public void add(Emp emp) {
// 根据员工的id得到该员工应当加入到哪条链表
int empLinkedListNo = hashFun(emp.id);
// 将emp添加到对应链表中
empLinkedListArrays[empLinkedListNo].add(emp);
}
// 遍历所有的链表,遍历哈希表
public void list() {
for (int i = 0; i < size; i++) {
empLinkedListArrays[i].list(i);
}
}
//根据输入的id查找雇员
public void findEmpById(int id) {
//使用散列函数确定到哪条链表查找
int empLinkedListNo = hashFun(id);
Emp emp=empLinkedListArrays[empLinkedListNo].findEmpById(id);
if(emp!=null) {//找到
System.out.println("在第"+(empLinkedListNo+1)+"条链表中找到雇员"+id);
}else {
System.out.println("在哈希表中,没有找到该雇员");
}
}
// 编写一个散列函数,使用取模法
public int hashFun(int id) {
return id % size;
}
}