【架构师之路】哈希函数优化终极方案,完美哈希函数的探索与思考

前言

哈希表是⼀种查找性能⾮常优异的数据结构,它在计算机系统中存在着⼴泛的应⽤。

尽管哈希表理论上的查找时间复杂度是 O(1),但不同的哈希表在实现上仍然存在巨⼤的性能差异。

下面是jdk里的一些hash表的测试情况

Key数量 碰撞率 JDK HashMap内存占用 FastUtil内存占用
100万 20% 1.2GB 860MB
100万 50% 2.8GB 1.4GB

我们发现,hash频繁碰撞也有可能将查询时间复杂度从O(1)退化为O(n),空间复杂度也会伴随上升。

这是因为,一般哈希表对哈希冲突的处理会增加额外的分⽀跳转和内存访问,这会让流⽔线式的CPU指令处理效率变差。当遇到哈希冲突时,我们常见到的解决⽅案有:开放寻址法拉链法⼆次哈希法

场景

本文所有代码均用kotlin展示,不习惯者可以用AI转一下java

现在我们有个如下的函数

class EntityWrapperKey<HASH>(value: EntityWrapper<*, HASH>) {
   
   

    val pk: Serializable = value.getPrimaryKey()

    val clazz: Class<*> = value.javaClass

    override fun equals(other: Any?): Boolean {
   
   
        if (this === other) return true
        if (other?.javaClass != javaClass) return false

        other as EntityWrapperKey<HASH>

        if (pk != other.pk) return false
        if (clazz != other.clazz) return false

        return true
    }
}

我们先实现一下标准哈希计算模式,满足基本需求

	// 使用31作为乘法因子,结合pk和clazz的哈希码
    override fun hashCode(): Int {
   
   
        var result = pk.hashCode()
        result = 31 * result + clazz.hashCode()
        return result
    }

缺点:

  • 复合主键深度问题:若 pk 是嵌套对象,其 hashCode() 可能未覆盖所有字段(如使用默认对象哈希)
  • 乘数选择局限:31 作为经典乘数在小数据量表现良好,但在百万级数据下碰撞率可能超过 15%
  • 计算效率:两次乘法操作在频繁调用时可能成为性能瓶颈

优化方案与代码实现

怎么能完全规避哈希冲突?那么有没有完美哈希函数(perfect hash function)呢?

1. 预计算哈希值(内存换性能)

0x9E3779B9 是一个非常著名的常数,在哈希函数和伪随机数生成器中广泛使用。这个常数源自于黄金分割比例。在计算机科学中,这个常数通常用于确保哈希函数的输出分布更加均匀,减少冲突的概率。

class EntityWrapperKey<HASH>(value: EntityWrapper<*, HASH>) {
   
   
    private val cachedHash by lazy {
   
    computeOptimizedHash() }
    
    private fun computeOptimizedHash(): Int {
   
   
        var hash = 0x9E3779B9 // 黄金分割常数 
        hash = hash * 31 + clazz.hashCode()
        hash = hash xor (pk.hashCode().rotateLeft(5)) // 位操作混合
        return hash
    }

    override fun hashCode() 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值