哈希表是根据 key value 来直接进行访问的数据结构(数组+链表),它通过把关键码值映射到表中一个位置来访问记录,加快查找速度。这个进行映射的函数叫做散列函数。
1 哈希表结构
哈希表的快速访问是通过散列函数+数组实现的,对于存入的每一个对象,散列函数会计算出一个值,作为放入位置的下标;在查找的时候,同样通过散列函数计算出下标,实现快速定位。对于拥有相同哈希值的不同元素,对应数组的同一下标,它们存放在同一链表中。哈希表中的元素分布比较均匀时,查询效率高。
以如下哈希表为例,假设数组大小 size = 5
,散列函数计算方法为 value % size
。
存入 0 时,哈希值为 0%5 = 0,则存入数组中下标 0 的位置,且 0 作为该处链表的第一个节点;
存入 1 时,哈希值为 1%5 = 1,则存入数组中下标 1 的位置,且 1 作为该处链表的第一个节点;
.
.
.
存入 4 时,哈希值为 4%5 = 4,则存入数组中下标 4 的位置,且 4 作为该处链表的第一个节点;
存入 5 时,哈希值为 5%5 = 0,则存入数组中下标 0 的位置,该位置已经存入的有元素了,所以将 5 加入到该处链表的尾部。
其他元素同理。
2 代码实现
以学生节点为例,拥有 name 和 id 两个属性,hash 值通过 id 计算。
package com.datastructure.hashtable;
public class HashTable {
//该数组存放学生链表
private StuLinkedList[] stuLinkedList;
//数组大小,也是哈希表大小
private int size;
HashTable(int size){
this.size = size;
stuLinkedList = new StuLinkedList[size];
//对数组中每一个链表进行初始化
for (int i=0; i<size; ++i) {
stuLinkedList[i] = new StuLinkedList();
}
}
//向哈希表中添加元素
public void add(String name, int id) {
//通过散列函数计算在数组中的位置
int stuLinkedNum = hashFun(id);
//将元素添加到对应位置的链表中
stuLinkedList[stuLinkedNum].add(name, id);
}
//在哈希表中查找元素
public Stu findStuById(int id) {
//通过散列函数计算在数组中的位置
int stuLinkedNum = hashFun(id);
//在对应位置的链表中查找元素
Stu stu = stuLinkedList[stuLinkedNum].findStuById(id);
System.out.printf("id 为 %d 的学生", id);
if (stu == null) {
System.out.println("没有找到");
return null;
} else {
System.out.printf("姓名为 %s", stu.name);
return stu;
}
}
public void show() {
for(int i=0; i<size; ++i) {
System.out.print("下标 " + i +", ");
stuLinkedList[i].show();
}
System.out.println();
}
//哈希函数
private int hashFun(int id) {
return id % size;
}
}
class StuLinkedList {
//头指针,指向第一个 Student
private Stu head;
StuLinkedList() {
this.head = null;
}
//向链表中添加元素
public void add(String name, int id) {
if(head == null) {
head = new Stu(name, id);
return;
}
Stu curStu = head;
while (curStu.next != null) {
curStu = curStu.next;
}
curStu.next = new Stu(name, id);
}
//在链表中查找元素
public Stu findStuById(int id) {
if (head == null) {
System.out.println("链表为空");
return null;
}
//遍历链表
Stu curStu = head;
while (curStu != null) {
if (curStu.id == id) {
return curStu;
}
curStu = curStu.next;
}
return null;
}
public void show() {
if (head == null) {
System.out.println("链表为空");
return;
}
System.out.print("链表信息为:");
Stu curStu = head;
while (curStu != null) {
System.out.printf("id = %d, name = %s ;\t", curStu.id, curStu.name);
curStu = curStu.next;
}
System.out.println();
}
}
//定义学生节点
class Stu {
//学生姓名
String name;
//学生 id
int id;
Stu next;
Stu() {
this.name = null;
this.id = id;
this.next = null;
}
Stu(String name, int id) {
this.name = name;
this.id = id;
this.next = null;
}
}
public class HashTable {
private StuLinkedList[] stuLinkedList;
private int size;
HashTable(int size){
this.size = size;
stuLinkedList = new StuLinkedList[size];
//对数组中每一个链表进行初始化
for (int i=0; i<size; ++i) {
stuLinkedList[i] = new StuLinkedList();
}
}