散列表-思考

本文探讨工业级散列表的关键特性,包括快速操作、合理内存占用和稳定性能。深入解析散列函数设计,如装载因子、动态扩容及冲突解决,以JDK HashMap为例,展示其巧妙的hash函数实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

何为一个工业级的散列表?工业级的散列表应该具有哪些特性?

支持快速的查询、插入、删除操作
内存占用合理,不能浪费过多的内存空间
性能稳定,极端情况下,散列表的性能也不会退化到无法接受的情况

如何实现这样一个散列表呢?

设计一个合适的散列函数;
定义装载因子阈值,并且设计动态扩容策略;
选择合适的散列冲突解决方法。


-HashMap
int hash(Object key) {
int h = key.hashCode();
return (h ^ (h >>> 16)) & (capitity -1); //capicity 表示散列表的大小
}

先补充下老师使用的这段代码的一些问题:在JDK HashMap源码中,是分两步走的:

  1. hash值的计算,源码如下:
    static final int hash(Object key) {
    int hash;
    return key == null ? 0 : (hash = key.hashCode()) ^ hash >>> 16;
    }

  2. 在插入或查找的时候,计算Key被映射到桶的位置:
    int index = hash(key) & (capacity - 1)


JDK HashMap中hash函数的设计,确实很巧妙:

首先hashcode本身是个32位整型值,在系统中,这个值对于不同的对象必须保证唯一(JAVA规范),这也是大家常说的,重写equals必须重写hashcode的重要原因。

获取对象的hashcode以后,先进行移位运算,然后再和自己做异或运算,即:hashcode ^ (hashcode >>> 16),这一步甚是巧妙,是将高16位移到低16位,这样计算出来的整型值将“具有”高位和低位的性质

最后,用hash表当前的容量减去一,再和刚刚计算出来的整型值做位与运算。进行位与运算,很好理解,是为了计算出数组中的位置。但这里有个问题:
为什么要用容量减去一?
因为 A % B = A & (B - 1),所以,(h ^ (h >>> 16)) & (capitity -1) = (h ^ (h >>> 16)) % capitity,可以看出这里本质上是使用了「除留余数法」

综上,可以看出,hashcode的随机性,加上移位异或算法,得到一个非常随机的hash值,再通过「除留余数法」,得到index,整体的设计过程与老师所说的“散列函数”设计原则非常吻合!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值