一、前言
需要代码的可以去gitee 上取,包括java 基础,数据结构,android, java swing ,java web项目(写的很杂,感谢光临):
https://gitee.com/lzhlovelh
在学习数据结构的哈希表时候,先看完视频,过了几天后记录自己手写哈希表,以便增加自己的映像,顺便写下自己理解。
学习hash表的前提需要了解链表和数组
二、什么是hash表
百度百科:
散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
通俗理解:
就相当于一张表格,表格里的元素通过一定的规律存放在表中。
三、代码和分析
数据结构:数组+链表
hash表存储学生信息
学生类(Student)
package com.lzh.hash;
/**
* @author lzh
* @create 2022-08-07 17:07
*
* Student类:每一个Student都有自己的姓名和id(简单点有两个属性)
*/
public class Student {
private int id;
private String name;
private Student next;// 指向下一个节点
public Student(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Student getNext() {
return next;
}
public void setNext(Student next) {
this.next = next;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", next=" + next +
'}';
}
}
学生类准备好之后,创建链表类
插入方法写了两个,一个是add,没有做任何限制,另外一个是addByOrder,是按照顺序插入
package com.lzh.hash;
/**
* @author lzh
* @create 2022-08-07 17:08
* <p>
* StudentLinkedList为链表
*/
public class StudentLinkedList {
private Student head;// 头指针
/**
* 添加元素,没有按顺序添加,也可以添加重复的元素
*
* @param student 要添加的节点
*/
public void add(Student student) {
// 如果链表的头结点为null,就将将要添加的student节点添加到头节点
if (head == null) {
head = student;
return;
}
Student currentStu = head; // 辅助指针,用来遍历链表
while (true) {
if (currentStu.getNext() == null) { // 如果遍历的节点的下一个节点为null
currentStu.setNext(student); // 添加到currentStu的下一个节点
break;
}
currentStu = currentStu.getNext(); // 如果当前的节点的下一个节点不为null, 则移动辅助指针
}
}
/**
* 按id顺序添加元素
*
* @param student
*/
public void addByOrder(Student student) {
// 如果链表的头结点为null,就将将要添加的student节点添加到头节点
if (head == null) {
head = student;
return;
}
Student currentStu = head; // 辅助指针,用来遍历链表
while (true) {
// 如果插入的元素大于当前遍历到的元素
if (student.getId() > currentStu.getId()) {
if (currentStu.getNext() != null) {
if (currentStu.getNext().getId() > student.getId()) {
student.setNext(currentStu.getNext());
currentStu.setNext(student);
break;
}
} else {
currentStu.setNext(student);
break;
}
} else if (student.getId() == currentStu.getId()) {
// 更新
currentStu.setName(student.getName());
break;
} else {
// 头节点比插入的节点大
student.setNext(head);
head = student;
break;
}
currentStu = currentStu.getNext(); // 如果当前的节点的下一个节点不为null, 则移动辅助指针
}
}
/**
* 通过student 的 id 进行删除
*
* @param id
* @param i 第几条链表
*/
public void del(int id, int i) {
// 如果链表为空
if (head == null) {
System.out.println("第" + i + "链表为空,无法删除");
return;
}
// 考虑如果删除的是头节点
if (head.getId() == id) {
head = head.getNext();
return;
}
// 如果删除的不是头节点
Student currentStu = head;
Student currentStuNext = currentStu.getNext();// 指向currentStu的下一个节点
while (true) {
// 此条链表中如果存在此id
if (currentStuNext.getId() == id) {
currentStu.setNext(currentStuNext.getNext());
break;
}
// 如果currentStuNext为null,则表明已经遍历完此链表了,没有找到此id的节点
if (currentStuNext.getNext() == null) {
System.out.println("没有此id的学生,删除无效");
break;
}
currentStu = currentStu.getNext();
currentStuNext = currentStuNext.getNext();
}
}
/**
* 更新节点的信息
*/
public void update(int id, String name, int i) {
if (head == null) {
System.out.println("第" + i + "链表为空,更改失败");
return;
}
Student currentStu = head;
while (true) {
// 找到id
if (currentStu.getId() == id) {
// 更改此id学生的姓名
currentStu.setName(name);
break;
}
if (currentStu.getNext() == null) {
System.out.println("没有此id学生,更改无效");
break;
}
currentStu = currentStu.getNext();
}
}
/**
* 用来显示链表的所有节点
*
* @param i 传入遍历时的i的值
*/
public void list(int i) {
if (head == null) {
System.out.println("第" + (i + 1) + "链表为空");
return;
}
System.out.printf("第%d条链表", (i + 1));
Student currentStu = head; // 辅助指针,用来遍历链表
while (true) {
System.out.printf("=> id=%d name=%s\t", currentStu.getId(), currentStu.getName());
if (currentStu.getNext() == null) {
break;
}
currentStu = currentStu.getNext();
}
System.out.println();
}
}
随后就是供外界使用的HashTableSelf类了,因为链表是不直接提供给外界使用的(对链表进行封装),所以提供一个类即HashTableSelf供外界使用。
package com.lzh.hash;
/**
* @author lzh
* @create 2022-08-07 17:09
* <p>
* 此类供外访问使用
*/
public class HashTableSelf {
// 定义一个StudentLinkedList的数组,存放每一条链表
private StudentLinkedList studentLinkedList[];
// 声明一个数组大小,通过构造器去赋值
private int size;
public HashTableSelf(int size) {
this.size = size;
this.studentLinkedList = new StudentLinkedList[size];
// 为每个数组的元素赋值,否则每个数组的每个元素都为null. 会报空指针异常
for (int i = 0; i < size; i++) {
studentLinkedList[i] = new StudentLinkedList();
}
}
/**
* 供外访问的添加方法
*
* @param student
*/
public void add(Student student) {
// 获取到当前元素应该在哪一条链表
int hashCode = hashCode(student.getId());
studentLinkedList[hashCode].add(student);
}
/**
* 根据id删除元素
*
* @param id
*/
public void del(int id) {
// 获取到当前元素应该在哪一条链表
int hashCode = hashCode(id);
studentLinkedList[hashCode].del(id, hashCode);
}
public void update(int id, String name) {
// 获取到当前元素应该在哪一条链表
int hashCode = hashCode(id);
studentLinkedList[hashCode].update(id, name, hashCode);
}
/**
* 供外访问的显示链表方法
*/
public void list() {
for (int i = 0; i < size; i++) {
studentLinkedList[i].list(i);
}
}
/**
* 映射规则
*
* @return 返回的值表示在该元素在数组中存放的位置
*/
public int hashCode(int id) {
return id % size;
}
}
最后就是测试代码,main函数:
package com.lzh.hash;
import java.util.Scanner;
/**
* @author liuzhihao
* @create 2022-08-07 17:51
*
* 控制台用来测试
*/
public class Console {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("======请输入您的操作======");
System.out.println("add: 添加学生");
System.out.println("del: 添加学生");
System.out.println("update: 添加学生");
System.out.println("list: 查询所有");
System.out.println("exit: 退出系统");
String input = scanner.next();
switch (input) {
case "add":
System.out.print("请输入添加学生的id:");
int inputId = scanner.nextInt();
System.out.print("请输入添加学生的name:");
String inputName = scanner.next();
Student inputStudent = new Student(inputId, inputName);
hashTableSelf.add(inputStudent);
break;
case "del":
System.out.print("请输入要删除学生的id:");
int delId = scanner.nextInt();
hashTableSelf.del(delId);
break;
case "update":
System.out.print("请输入添加学生的id:");
int updateId = scanner.nextInt();
System.out.print("请输入添加学生的name:");
String updateName = scanner.next();
hashTableSelf.update(updateId, updateName);
break;
case "list":
hashTableSelf.list();
break;
case "exit":
System.out.println("退出系统成功!");
scanner.close();
System.exit(0);
default:
System.out.println("请正确输入操作");
break;
}
}
}
}
四、测试结果
4.1 运行起来
上述数据是测试时手动加入。
4.2 add
4.3 del
4.4 update
4.5 list
最后list就是显示链表中所有的元素