1. 哈希表是一种数据结构,其由数组+链表或数组+二叉树构成。哈希表最大的特征是利用散列函数将关键码值进行映射,并根据关键码值进行直接访问。利用哈希表可以极大提高查找速度。
2. 哈希表的具体结构:
以数组+链表为例,哈希表包含一个由链表构成的数组,数组中每个元素是一条链表的头节点,如下图所示
3. 散列函数:散列函数是哈希表的关键,通过散列函数才能快速确定要查找的值位于哪一条链表。散列函数有很多种,目前只学了简单的取模法,日后再行补充。例如我们以int型的ID作为关键码值,则对ID进行取模,取模得到的值就是其所在的链表序号。例如链表数组长度为5(即共有五条链表),ID为7,取模为2,则该内容应存储于第3条链表中。
4. 哈希表的代码实现:
import java.util.Scanner;
public class HashTableTest {
public static void main(String[] args) {
HashTable testHasTab = new HashTable(7);
testHasTab.add(new Employee(3,"张三"));
testHasTab.add(new Employee(7,"张三"));
testHasTab.add(new Employee(1,"张三"));
testHasTab.add(new Employee(9,"张三"));
Scanner scan = new Scanner(System.in);
while(true){
System.out.println("添加:输入add");
System.out.println("显示:输入show");
System.out.println("查找:输入find");
System.out.println("退出系统:输入exit");
String input = scan.next();
switch (input){
case "add":
System.out.println("输入ID");
int id = scan.nextInt();
System.out.println("输入姓名");
String name = scan.next();
testHasTab.add(new Employee(id,name));
break;
case "exit":
break;
case "show":
testHasTab.showTable();
break;
case "find":
System.out.println("请输入ID");
int findId = scan.nextInt();
Employee emp = testHasTab.findEmpByID(findId);
System.out.println("您查找的用户信息:"+emp.getId()+"--"+emp.getName());
break;
default:
break;
}
}
// testHasTab.add(new Employee(1,"张三"));
}
}
//创建HashTable,用于管理多条链表
class HashTable{
private EmployeeLinkedList[] employeeLinkedListArray;
private int size;//有多少条链表
public HashTable(int size){
this.size = size;
employeeLinkedListArray = new EmployeeLinkedList[this.size];
//链表数组中的每一条链表都需要单独初始化,否则会出现空指针异常
for (int i = 0; i < size; i++) {
employeeLinkedListArray[i] = new EmployeeLinkedList();
}
}
//添加员工
public void add(Employee emp){
//利用散列函数判断该员工应该被添加到哪一条链表
int employeeLinkedListNumber = hasFun(emp.getId());
employeeLinkedListArray[employeeLinkedListNumber].addEmp(emp);
}
public Employee findEmpByID(int id){
int employeeLinkedListNumber = hasFun(id);
Employee emp = employeeLinkedListArray[employeeLinkedListNumber].findEmpByID(id);
return emp;
}
public void showTable(){
for (int i = 0; i < size; i++) {
employeeLinkedListArray[i].showList(i);
}
}
//编写散列函数,最简单的是取模法
public int hasFun(int id){
return id % size;
}
}
class EmployeeLinkedList{
private Employee head = new Employee(0,"");
public void addEmp(Employee emp){
Employee temp = head;
while(temp.getNext() != null){
if(temp.getNext().getId() > emp.getId()){
emp.setNext(temp.getNext());
break;
}
temp = temp.getNext();
}
temp.setNext(emp);
}
public void deleteEmp(int id){}
public Employee findEmpByID(int id){
Employee curEmp = head;
while(curEmp.getId() != id){
curEmp = curEmp.getNext();
}
if(curEmp.getId() == id){
return new Employee(curEmp.getId(),curEmp.getName());
}else{
return null;
}
}
public void showList(int no){
if (head.getNext() == null){
System.out.println("链表"+no+"为空");
return;
}
Employee temp = head.getNext();
System.out.println("链表"+no+"包含的信息:");
while(temp!=null){
System.out.print("=>"+temp.getId()+"--"+temp.getName());
temp = temp.getNext();
}
System.out.println();
}
}
class Employee{
private int id;
private String name;
private Employee next;
public Employee(){}
public Employee(int id,String name){
this.id = id;
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Employee getNext() {
return next;
}
public void setNext(Employee next) {
this.next = next;
}
}
5. 学习哈希表中的教训与心得:
(1)哈希表在创建以后,必须对表中每一条链表进行单独的初始化,否则在后续调用哈希表时,会出现空指针异常。
(2)哈希表的关键在于散列函数,所以哈希表其实又称为散列表。在存储和查找时均需利用散列函数来对待存储的内容进行位置映射。