原创

哈希表

温馨提示:
本文最后更新于 2022年06月18日,已超过 8 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我

哈希表

1. 基本介绍

要了解哈希表,首先就要知道为什么会出现哈希表这个东西,哈希表是用来干什么的?

这就要从数据的访问开始,我们都知道现在网站的数据基本上都是存储在数据库当中,当用户发送请求给服务器的时候,服务器就会从数据库中查询相应的数据而返回给用户,但是有没有想过,当大量用户同时访问数据库会造成什么样的问题呢?很显然,数据库也是有承载限度的,一旦超过了这个限度,数据库将会奔溃,这样将带来严重的后果,并且即使数据库不会奔溃,大量的数据库访问也会拖慢数据库的响应速度,从而降低用户的体验,那有没有什么方法能够解决现在这个问题呢?答案是有,从数据库刚开始出现不就就有人发现了这样的问题,提出了缓存的概念,在数据库和用户之间再套上一层,缓存层,当用户需要访问数据库的时候不是直接查询数据库而是先查询缓存层,如果缓存层没有才会去查询数据库,这样对于一些静态的数据就可以预先加载人缓存,从而提高访问速度,目前常见的缓存产品主要有

  • memcached:分布式缓存的标配
  • Redis:新一代的分布式缓存,有替代memcached的趋势

但是今天要讲的不是这两款,而是哈希表,这是一种数据结构,在早期没有出现缓存产品的时候,哈希表就扮演了缓存这一角色,目前对于少量的数据任然可以使用哈希表来存储将少量访问次数较多的数据存放在哈希表中就可以加快访问的速度,

哈希表的特点

哈希表主要由三部分组成:存储数据的一个类,连接每一个类的一个链表,用来存储链表的一个一维数组

在我看来哈希表的主要实现原理其实还是链表,使用链表将每一个数据类连接起来

哈希表实现图示

image-20220430090614646

其实java也自带了哈希表,例如hashmap,这些的功能和我们将要编写的哈希表的功能是很相似的,之所以手动编写,是为了更好的理解哈希表的工作原理,方便之后我们使用哈希表

哈希表的代码实现

  1. 数据存储单元的编写
class Emp{
    private int id;
    private String name;
    private double salary;
    //有一个指针指向下一个节点
    private Emp next;

    public Emp(int id, String name, double salary) {
        this.id = id;
        this.name = name;
        this.salary = salary;
    }

    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 double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public Emp getNext() {
        return next;
    }

    public void setNext(Emp next) {
        this.next = next;
    }

    @Override
    public String toString() {
        return "Emp{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", salary=" + salary +
                '}';
    }
}
  1. 连接每一个数据单元链表的编写
class HashTableLink{
    //存在一个头指针指向第一个emp
    private Emp head;
    //添加方法
    public void add(Emp emp){
        //判断头结点是否为空
        if(head == null){
            head = emp;
            return;
        }
        Emp curHead = head;
        while(curHead.getNext() !=null){
            curHead = curHead.getNext();
        }
        //跳出循环说明到链表最后
        curHead.setNext(emp);
    }
    //遍历方法
    public void list(){
        if(head == null){
            System.out.println("表为空,无法遍历");
        }
        Emp curHead = head;
        while(true){
            System.out.println(head);
            if(curHead.getNext()!=null){
                curHead = curHead.getNext();
            }else{
                break;
            }
        }
    }
}
  1. 存储链表的数组编写
class HashTable{
    //创建一个数组用来存储链表
    private HashTableLink[] array;
    int size;
    //初始化数组的大小
    public HashTable(int size) {
        this.size = size;
        array = new HashTableLink[size];
        //初始化数组中的每一个元素,这一点特别重要,否则之后将会出现空指针异常
        for(int i = 0;i<size;i++){
            a[i] = new HashTableLink();
        }
    }
    //编写最后的实现方法
    public int hash(int id){
        return id%size;
    }
    public void add(Emp emp){
        int emplooyNo = hash(emp.getId());
        array[emplooyNo].add(emp);
    }
    public void list(){
        for(int i = 0;i<size;i++){
            array[i].list();
        }
    }
}

测试运行

public class hashTableTest {
    public static void main(String[] args) {
        //通过测试来看看代码的正确性
        HashTable test= new HashTable(2);
        test.add(new Emp(1001,"张三",10000.000));
        test.add(new Emp(1002,"张4",3450000.000));
        test.add(new Emp(1003,"张5",14360000.000));
        test.add(new Emp(1004,"张6",10056400.000));
        test.add(new Emp(1005,"张7",10034300.000));
        System.out.println("=====================");
        test.list();
    }
}

可以看到代码正常运行

正文到此结束