哈希表 散列表

在链表和数组中,如果我们想要查找一个特定的数值,时间复杂度都是O(n)。那有什么办法能用最快的速度来找到一个特定的元素呢?答案便是“哈希表”。在哈希表中,查找、删除、新增一个元素,时间复杂度都是O(1)。

哈希表(也叫散列表)是一种以键值对的方式来存储元素的数据结构,提供快速的插入和查找功能。哈希表基于数组存储数据,因此能在O(1)时间内定位数据。关键字值(key)通过哈希函数映射为数组下标。缺点就是数组创建后容量固定,如果数据较多需要不断扩展其长度。

一般情况下,为了防止计算得到的哈希值超出tableSize,通过哈希函数拿到哈希值后,还会对哈希值再做一次取模操作:

index = hashCode(key) % tableSize

哈希冲突

上面已经讲了,哈希表中是通过哈希函数计算数组下标的,不同的key计算后有可能得到相同的结果,也就会落到哈希表的同一个索引下标的位置,这种现象叫做哈希冲突。

一般哈希冲突有两种解决方法, 拉链法线性探测法

哈希表的实现方式——拉链法

我们都知道数组的特点是寻址容易,但是插入和删除数据困难。而链表的特点是寻址困难,但是插入和删除数据容易。拉链法实现的哈希表综合了两者的特性,是一种寻址容易,插入和删除也容易的数据结构。拉链法实现的哈希表由数组和一堆链表组成,其结构如下图所示:
在这里插入图片描述
从上图可以看到,哈希表的左侧是数组,数组的每个成员包括一个指针,指针指向一个链表的头结点。链表可能为空,也可能有多个节点。当出现哈希冲突时,只需要把新节点添加到对应链表最后即可。

我们使用拉链法实现的哈希表存储键值对时,首先会使用哈希函数计算key的哈希值,通过哈希值找到对应的数组下标,然后将键值对添加到对应的链表中。寻找元素时,也是根据键的哈希值,找到特定链表中对应的值。

哈希表的操作

  • get(K key):通过特定的关键字拿到其所对应的值
  • add(Key key, Value value):将一对新的键值对加入哈希表
  • remove(Key key):通过关键字,删除哈希表中的键值对
  • getSize():获取当前键值对的数量
  • isEmpty():查看哈希表是否为空

java实现哈希表

我们以key为int类型,value为String类型实现拉链法的哈希表。

定义哈希节点

public class HashMap {
   
    /**
     * 定义哈希节点
     */
    static class HashNode {
   
        Integer key;
        String value;
        HashNode next;

        public HashNode(Integer key, String value) {
   
            this.key = key;
            this.value = value;
        }
    }

    // 拉链法左边的数组
    private List<HashNode> arrays;
    // 哈希数组的长度
    private int tableSize;
    // 当前存储的键值对数量
    private int dataSize;

    public HashMap() {
   
        arrays = new ArrayList<>();
        tableSize = 10;
        dataSize = 0;
        // 初始化指针指向的链表
        for (int i = 0; i < tableSize; i++) {
   
            arrays.add(i, null);
        }
    }
}

其中HashNode就是键值对节点的定义,包含了键(key)、值(value)和指向下一个链表节点的next指针。arrays为上图中拉链法左边的数组,数组中存储着指向对应链表的指针。tableSize为哈希数组的长度,初始时初始化为10。dataSize为当前存储的键值对的数量,初始化为0。

获取数组下标值方法

    private int getArrayIndex(Integer key) {
   
        int hashCode = key.hashCode();
        return hashCode % tableSize;
    }

add方法

add方法将一对键值对存储到哈希表中。在存储之前需要先判断是否已经存储过相同key的键值对,如果已经存储过,则只需要更新value即可;如果相同key的键值对不存在,除了需要把键值对存储到哈希表中,为了保证查询效率,还需要判断哈希表是否需要扩容。

    /**
     * 核心方法 add:新增键值对
     *
     * @param key
     * @param value
     */
    public void add(Integer key, String value) {
   
        int arrayIndex = getArrayIndex(key);
        // 获取链表头节点
        HashNode header = arrays.get(arrayIndex);
        HashNode node = new HashNode(key, value);
        // 链表不为空的场景下,需要遍历链表查看是否已经存在同样的key
        while (header != null) {
   
            // 已经存在相同的key,则更新value
            if (header.key.equals(key)) {
   
                header.value = value;
                return;
            }
            header = header.next;
        }
        // 不存在相同key的场景,将新节点插入到链表的最前面(链表为空的场景同样走这里)
        header = arrays.get(arrayIndex);
        node.next = header
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值