哈希表(散列)-Google
哈希表的基本介绍
散列表(Hash table 也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构,也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度,这个映射函数叫做散列函数,存放记录的数组叫做散列表
下面的数组,变成了一个链表数组
我们的java程序向数据库查数据,但是太频繁会造成内存的开销和浪费资源,数据量过大的时候,速度也会变慢,压力也会很大.所有现在很多的公司有缓存层,现在缓存的产品有很多,redis,Memcache等,但是我们在一二十年前是没有缓存产品的,我们就是自己写的,用(1)哈希表
数组+链表
数组+二叉树
看一个实际的需求
有一个公司,当有新的员工来报道的时候,要求将该员工的信息加入(id,性别,年龄,住址),当输入该员工的id时,要求查到该员工的所有信息
要求:不要用数据库尽量节省内存,速度越快越好=>哈希表(散列)
添加时保证按照id从低到高的顺序插入.
每一个链表有一个头指针指向当前链表的第一个雇员!
代码如下
package com.atguigu.hashtab;
import java.util.Scanner;
public class HasshTabDemo {
public static void main(String[] args) {
//创建一个哈希表,假定有7条链表
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 "find":
System.out.println("请输入要查找的id");
id=scanner.nextInt();
hashTab.findEmpByIds(id);
break;
case "exit":
scanner.close();
System.exit(0);
default:
break;
}
}
}
}
//开始创建HashTab 管理多条链表
class HashTab{
private EmpLinkedList[] empLinkedListsArray;
private int size; //表示共有多少条链表
//首先要一个构造器
public HashTab(int size){
this.size=size;
//初始化我们empLinkedListsArray链表
empLinkedListsArray=new EmpLinkedList[size];
//这时不要忘记分别初始化我们的每一条链表(上面初始化的是怎个哈希表那个数组,
// 下面初始化的是我们数组元素,因为我们的数组元素是链表组成的
for(int i=0;i<size;i++){
empLinkedListsArray[i]=new EmpLinkedList();
}
}
//真正的添加雇员
public void add(Emp emp){
//首先我要根据员工的id得到该员工应当添加到那条链表
int empLinkedListNO=hashFun(emp.id);
//将emp添加到对应的链表中
empLinkedListsArray[empLinkedListNO].empAdd(emp);
}
//遍历所有的链表(我们的哈希表)
public void list(){
for(int i=0;i<size;i++){
empLinkedListsArray[i].showList(i);
}
}
//根据输入的id查找雇员
public void findEmpByIds(int id){
//使用散列函数确定到那条链表查找
int empLinkedListNO=hashFun(id);
Emp emp=empLinkedListsArray[empLinkedListNO].findEmpById(id);
if(emp!=null){
System.out.println("在第"+(empLinkedListNO+1)+"条链表中找到该雇员");
}else {
System.out.println("在哈希表中没有找到该雇员");
}
}
//编写一个散列函数,使用一个简单的取模法来处理这个
public int hashFun(int id){
return id%size;
}
}
//表示一个雇员
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是直接指向第一个雇员的(Emp)
private Emp head;//默认是空的
//添加雇员到链表
//说明
/**
* 1.假定添加雇员的时候,id是自增长的,即ID的分配总是从小到大
* 2,因此我们将该雇员直接加入到本链表的最后即可
* @param emp
*/
public void empAdd(Emp emp){
//如果是添加这条链表的第一个雇员
if(head==null){
head=emp;//head直接指向employee就型
return;
}
//如果不是第一个雇员,则使用一个辅助指针,帮助定位到最后
Emp curEmp=head;
while (true){
if(curEmp.next==null) {//说明到了最后
break;
}
curEmp=curEmp.next;//后移
}
//退出时候,直接减emp加入链表的最后
curEmp.next=emp;
}
//遍历链表的雇员信息
public void showList(int no){
if(head==null){
//说明链表为空,返回就像
System.out.println();
System.out.println("当前链表"+(no+1)+"为空");
return;
}
System.out.print("当前链表"+(no+1)+"的信息为:");
Emp curEmp=head;//辅助指针
while (true){
System.out.print("==>id="+curEmp.id+" name==>"+curEmp.name);
if(curEmp.next==null){//说明curEmp已经是最后节点
break;
}
curEmp=curEmp.next;//后移
}
}
//根据ID查找雇员
/**
*
* @param id
* @return 找到返回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;
}
curEmp=curEmp.next;//后移
}
return curEmp;
}
}
上面的哈希表我们默认是添加到每一条链表的最后,但是我们还可以就是如果插入数据的时候id没有从小到大插入,但是又要是顺序的我们改怎么插入了.代码又是怎么样的呢?