1. 概述
散列表(Hash table,也叫哈希表),是根据 关键码值(Key value) 而直接进行访问的数据结构。也就是说,它通过把关键码值 映射 到表中一个位置来访问记录(也就是先找是存放在哪条链表上),以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
2. 案例
有一个公司,当有新的员工来报道时,要求将该员工的信息(id, 性别, 年龄, 住址) 加入,当输入该员工的 id 时,要求查找到该员工的所有信息。要求: ① 不使用数据库,尽量节省内存,速度越快越好;② 添加时,保证按照 id 从低到高插入。
2.1 思路分析
2.2 代码实现
public class HashTabDemo {
public static void main(String[] args) {
HashTab hashTab = new HashTab(7);
for (int i = 0; i < 10; i++) {
int num = (int) (Math.random()*1101);
hashTab.add(new Emp(num, "emp_"+num));
}
hashTab.list();
hashTab.add(new Emp(1, "emp_1"));
hashTab.add(new Emp(1, "emp_1"));
hashTab.add(new Emp(9999, "emp_9999"));
hashTab.findEmpById(1);
hashTab.deleteEmpById(1024);
hashTab.add(new Emp(9999, "emp_9999"));
hashTab.list();
}
}
class HashTab {
private EmpLinkedList[] empLinkedListArr;
private int size; // 数组大小(链表条数)
public HashTab(int size) {
super();
this.size = size;
this.empLinkedListArr = new EmpLinkedList[size];
// 上一行仅仅是创建了数组本身, 或者说只是创建了 size 个存放对象引用的空间(其值均为 null)
// 要初始化这些引用, 让他们分别指向具体的 EmpLinkedList 对象才行
for (int i = 0; i < size; i++)
empLinkedListArr[i] = new EmpLinkedList();
}
// 散列函数 (取模法)
public int hashFunc(int id) {
return id % size;
}
// 添加雇员
public void add(Emp emp) {
// 1. 根据员工id, 先得到该员工应加入 [哪一条链表/数组哪个索引] 下
int empLinkedListNo = hashFunc(emp.id);
// 2. 将emp添加到对应的链表中
empLinkedListArr[empLinkedListNo].add(emp);
}
// 根据 id 查找雇员
public void findEmpById(int id) {
Emp emp = empLinkedListArr[hashFunc(id)].findEmpById(id);
if (emp != null) System.out.println(emp);
else System.out.println("没有找到该雇员");
}
public void deleteEmpById(int id) {
Emp delEmp = empLinkedListArr[hashFunc(id)].deleteEmpById(id);
if (delEmp != null) System.out.println(delEmp + "删除成功!");
else System.out.println("删除失败!");
}
// 遍历所有的链表
public void list() {
for (int i = 0; i<size; i++) {
System.out.printf("empLinkedListArr[%d]: ", i);
empLinkedListArr[i].list();
}
System.out.println("---------------------------------------------------");
}
}
class EmpLinkedList {
// 首结点(不是头结点!)
public Emp head;
// 添加雇员
public void add(Emp emp) {
if (head == null) { // 是该链表的第 1 个结点
head = emp;
} else { // 不是第1个
if (emp.id < head.id) {
emp.next = head;
head = emp;
} else if (emp.id == head.id) {
System.out.println("id已存在, 添加失败!");
return;
} else {
Emp curEmp = head;
while (curEmp.next != null) {
if (curEmp.next.id > emp.id) {
break;
} else if (curEmp.next.id == emp.id) {
System.out.println("id 已存在, 添加失败!");
return;
}
curEmp = curEmp.next;
}
emp.next = curEmp.next;
curEmp.next = emp;
}
}
}
// 根据 id 查找雇员
public Emp findEmpById(int id) {
// 判断链表是否为空
if (head == null) return null;
Emp curEmp = head;
while (curEmp != null) {
if (curEmp.id == id)
return curEmp;
curEmp = curEmp.next;
}
// 到这了的话说明链表里没这 id 对应的雇员
return null;
}
public Emp deleteEmpById(int id) {
// 判断链表是否为空
if (head == null) return null;
Emp delEmp = null;
if (head.id == id) { // 待删雇员恰为首结点
delEmp = head;
head = head.next;
return delEmp; // !null → 找到了
} else { // 非首结点
Emp curEmp = head;
while (curEmp.next != null) {
if (curEmp.next.id == id) {
delEmp = curEmp.next;
curEmp.next = delEmp.next;
return delEmp; // !null → 找到了
}
curEmp = curEmp.next;
}
return delEmp; // null → 未找到拥有该 id 的雇员
}
}
// 遍历链表的雇员信息
public void list() {
if (head == null) {
System.out.println("该链表为空");
return;
} else {
Emp curEmp = head;
while (curEmp != null) {
System.out.print(curEmp + " → ");
curEmp = curEmp.next;
}
System.out.println();
}
}
}
class Emp {
public int id;
public String name;
public Emp next;
public Emp(int id, String name) {
super();
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "[id=" + id + ", name=" + name + "]";
}
}