概述
哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数。
来自百度百科
简单来说,哈希表就是一种数据结构,是一种非线性的结构,是一种表。
与之对应的有一个哈希函数,给定哈希函数一个值,通过此函数可快速查找到在表中与这个值对应的数据。
所以总的来说,哈希表存在的意义是为了加快查找速度,搭配哈希函数使用。
一般我们实现哈希表都是通过数组加链表来实现(具体结构和上图一样)。定义一个数组,再创建几个链表,创建链表的数量等于数组的容量。然后把链表放在数组中,把需要存储的数据放在链表中,至此便构成一个简单的哈希表。
实现一个哈希表
实现哈希表首先需要链表
创建一个链表
- 创建链表需要节点,首先我们创建节点
一个简单的带next的员工类,表示节点类 - 节点是对象,链表也是对象,所以需要创建一个链表类
此类就是链表类,链表类有几个属性和方法,此处我们仅实现他的增加、查找和遍历功能。
到这链表便创建成功
实现表
表的主体实现就上面这一部分代码
首先一个链表类型的数组,然后一个还需要一个表示数组容量的属性。
然后下面是表的构造函数,接收一个参数,用于初始化创建数组,同时用于为数组中添加链表,哈希表至此创建完毕。
但是我们要使用这个数据结构,所以他得有几个基本的方法,这些方法必须是建立在链表所存在的方法的基础上的,因为这个哈希表,从底层来讲,我们终归是操纵的是链表中的数据。
一个完整哈希表的构造和使用
哈希表的使用中,散列函数是无比重要的,正是他的存在,才能使我们的查找更加迅速
散列函数
哈希函数指将哈希表中元素的关键键值映射为元素存储位置的函数。
一般的线性表,树中,记录在结构中的相对位置是随机的,即和记录的关键字之间不存在确定的关系,因此,在结构中查找记录时需进行一系列和关键字的比较。这一类查找方法建立在“比较“的基础上,查找的效率依赖于查找过程中所进行的比较次数。 理想的情况是能直接找到需要的记录,因此必须在记录的存储位置和它的关键字之间建立一个确定的对应关系f,使每个关键字和结构中一个唯一的存储位置相对应。
来自百度百科
简单来说,散列函数就是为了方便在哈希表中进行查找或者存储而存在的函数。所有能提高哈希表查找速度的函数都叫散列函数。
这里,我们简单写一个散列函数,按照员工id号对数组容量取余来写一个散列函数,具体看下面
我们用员工id对数组容量进行取余,按照取余得到的数据来选择把此员工放在哪个链表。取余得到的数据一定小于或者等于数组的最大索引,当得到的数据与某个索引相同,则把这个员工放在对应索引位置的链表末端,这样正好能把一些不同的员工均匀的放在不同的链表中
对哈希表的查询也类似,首先通过取余,得到一个索引值,然后定位到对应索引位置的链表处,对那条链表进行遍历查询。
这里一个简单的散列函数
一个完整的哈希表的类应该有一些方法
我们简单的根据链表所拥有的方法来为哈希表添加一些方法:
测试
先简单的把数组容量设置为6
没有问题
全部代码
import java.util.Scanner;
//哈希表
//一种数据结构
public class Hashtable {
public static void main(String[] args) {
HashtableDemo hashtableDemo=new HashtableDemo(6);
Scanner scanner=new Scanner(System.in);
while (true) {
System.out.println("1,添加\n2,展示\n3,查找\n0,退出");
String string=scanner.next();
if (string.equals("1")){
int n;
String name,N;
while (true){
System.out.print("输入id");
N=scanner.next();
try {
n= Integer.parseInt(N);
System.out.print("输入姓名");
name=scanner.next();
break;
}catch (Exception e){
System.out.println("输入错误,请重新输入");
}
}
Employee employee=new Employee(n,name);
hashtableDemo.add(employee);
}else if (string.equals("2")){
hashtableDemo.list();
}else if (string.equals("3")){
int id;
String Id;
while (true){
System.out.print("输入id");
Id=scanner.next();
try {
id= Integer.parseInt(Id);
try {
hashtableDemo.Select(id);
}catch (Exception e){
System.out.println(e);
}
break;
}catch (Exception e){
System.out.println("输入错误,请重新输入");
}
}
}else if (string.equals("0")){
return;
}
}
}
}
class HashtableDemo{
private LinkdeList[] array;
private int size;
public HashtableDemo(int size) {//初始化哈希表
this.size=size;
array=new LinkdeList[size]; //创建数组
for (int i = 0; i < size; i++) { //初始化数组
array[i]=new LinkdeList();
}
}
public void add(Employee employee){ //添加员工
array[Hashfun(employee.getId())].add(employee); //经过散列函数处理后,调用链表的增加节点的方法
}
public void list(){ //遍历哈希表
for (int i = 0; i < size; i++) {
System.out.print("第"+(i+1)+"行:");
array[i].list(); //对数组的每个链表调用链表对应的遍历方法
}
}
public int Hashfun(int id){
return id%size;
} //散列函数
public void Select(int id){
System.out.println(array[Hashfun(id)].get(id)); //经过散列函数处理后,调用链表的查找方法
} //查找方法
}
class Employee{ //员工类,即链表节点
private int id;
private String name;
public Employee next=null;
public int getId() {
return id;
}
public String getName() {
return name;
}
public Employee(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
class LinkdeList{ //管理链表的类,等于说,这个类就是链表类,一个这样的对象代表着一条链表
private Employee head; private Employee temp; private Employee end;
//三个属性,head是头节点,temp是辅助节点用于遍历链表,end用于记录链表尾,用于后面方便加入数据
public void add(Employee employee){ // 添加节点
if (head==null){
head=employee;
end=head;
}else {
end.next=employee;
end=end.next;
}
}
public void list(){// 遍历
temp=head;
if (head==null){
System.out.println("无数据");
}
while (temp!=null){
System.out.println(temp);
temp=temp.next;
}
}
public Employee get(int id){// 查找
temp=head;
while (temp!=null){
if (temp.getId()==id){
return temp;
}
temp=temp.next;
}
throw new RuntimeException("未找到");
}
}