哈希表

作者: iOS小洁 | 来源:发表于2023-02-10 11:44 被阅读0次

    哈希表

    TreeMap分析

    时间复杂度(平均) 添加、删除、搜索:O(logn)

    特点 Key 必须具备可比较性 元素的分布是有顺序的

    在实际应用中,很多时候的需求 Map 中存储的元素不需要讲究顺序 Map 中的 Key 不需要具备可比较性

    不考虑顺序、不考虑 Key 的可比较性,Map 有更好的实现方案,平均时间复杂度可以达到 O(1) 那就是采取哈希表来实现 Map

    哈希表(Hash Table)

    添加、搜索、删除的流程都是类似的

    1. 利用哈希函数生成 key 对应的 index【O(1)】
    2. 根据 index 操作定位数组元素【O(1)】

    哈希表是【空间换时间】的典型应用 哈希函数,也叫做散列函数

    哈希表内部的数组元素,很多地方也叫 Bucket(桶),整个数组叫 Buckets 或者 Bucket Array

    哈希冲突(Hash Collision)

    解决哈希冲突的常见方法

    1. 开放定址法(Open Addressing) :按照一定规则向其他地址探测,直到遇到空桶
    2. 再哈希法(Re-Hashing) : 设计多个哈希函数
    3. 链地址法(Separate Chaining) : 比如通过链表将同一index的元素串起来

    JDK1.8的哈希冲突解决方案

    默认使用单向链表将元素串起来

    在添加元素时,可能会由单向链表转为红黑树来存储元素 比如当哈希表容量 ≥ 64 且 单向链表的节点数量大于 8 时

    当红黑树节点数量少到一定程度时,又会转为单向链表

    JDK1.8中的哈希表是使用链表+红黑树解决哈希冲突

    思考:这里为什么使用单链表? 每次都是从头节点开始遍历 单向链表比双向链表少一个指针,可以节省内存空间

    哈希函数

    哈希函数的实现步骤大概如下

    1. 先生成 key 的哈希值(必须是整数)

    2. 再让 key 的哈希值跟数组的大小进行相关运算,生成一个索引值

    为了提高效率,可以使用 & 位运算取代 % 运算【前提:将数组的长度设计为 2 的幂(2 n )】

    良好的哈希函数 让哈希值更加均匀分布 → 减少哈希冲突次数 → 提升哈希表的性能

    如何生成key的哈希值

    key 的常见种类可能有整数、浮点数、字符串、自定义对象

    不同种类的 key,哈希值的生成方式不一样,但目标是一致的

    尽量让每个 key 的哈希值是唯一的 尽量让 key 的所有信息参与运算

    在Java中,HashMap 的 key 必须实现 hashCode、equals 方法,也允许 key 为 null

    整数 :整数值当做哈希值 比如 10 的哈希值就是 10

    浮点数 :将存储的二进制格式转为整数值

    Long和Double的哈希值:高32bit 和 低32bit 混合计算出 32bit 的哈希值,(>>> 和 ^)

    字符串:

    关于31的探讨

    31不仅仅是符合2^n – 1,它是个奇素数(既是奇数,又是素数,也就是质数)

    素数和其他数相乘的结果比其他方式更容易产成唯一性,减少哈希冲突

    最终选择31是经过观测分布结果后的选择

    自定义对象作为key

    自定义对象作为 key,最好同时重写 hashCode 、equals 方法

    equals :用以判断 2 个 key 是否为同一个 key

    • 自反性:对于任何非 null 的 x,x.equals(x)必须返回true
    • 对称性:对于任何非 null 的 x、y,如果 y.equals(x) 返回 true,x.equals(y) 必须返回 true
    • 传递性:对于任何非 null 的 x、y、z,如果 x.equals(y)、y.equals(z) 返回 true,那么x.equals(z) 必须 返回 true
    • 一致性:对于任何非 null 的 x、y,只要 equals 的比较操作在对象中所用的信息没有被修改,多次调用 x.equals(y) 就会一致地返回 true,或者一致地返回 false
    • 对于任何非 null 的 x,x.equals(null) 必须返回 false

    hashCode :必须保证 equals 为 true 的 2 个 key 的哈希值一样 反过来 hashCode 相等的 key,不一定 equals 为 true

    不重写 hashCode 方法只重写 equals 会有什么后果? 可能会导致 2 个 equals 为 true 的 key 同时存在哈希表中

    装填因子

    装填因子(Load Factor):节点总数量 / 哈希表桶数组长度,也叫做负载因子

    在JDK1.8的HashMap中,如果装填因子超过0.75,就扩容为原来的2倍

    TreeMap vs HashMap

    何时选择TreeMap? 元素具备可比较性且要求升序遍历(按照元素从小到大)

    何时选择HashMap? 无序遍历

    LinkedHashMap

    在HashMap的基础上维护元素的添加顺序,使得遍历的结果是遵从添加顺序的

    关于使用%来计算索引

    建议把哈希表的长度设计为素数(质数) 可以大大减小哈希冲突

    不同数据规模对应的最佳素数,特点如下:每个素数略小于前一个素数的2倍 每个素数尽可能接近2的幂(2n )

    相关文章

      网友评论

        本文标题:哈希表

        本文链接:https://www.haomeiwen.com/subject/vdjkkdtx.html