漫谈数据结构——散列表的设计

130 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第24天,点击查看活动详情

前言

在上一篇文章中,我们学会了散列表的概念,以及散列表的查询效率是非常之高的。那么我们要如何设计一个性能优秀的散列表呢?

散列表的设计

在某些时候,如果有恶意的攻击者精心设计数据结构,让大量的数据都聚集在一个槽上,让散列表的查询效率退化成链表的查询效率,时间复杂度就由原先的O(1)退化成O(n)了,这样子查询操作就会消耗大量CPU或者线程资源,导致系统无法响应其他请求,从而达到拒绝服务攻击(DoS)的目的。这就是碰撞攻击的基本原理。散列表冲突性能急剧下降,并且能抵抗散列表碰撞攻击。所以散列函数的好坏也直接决定了散列表的性能。首先我们设计散列函数一定不能太复杂,如果复杂的散列函数,计算的时候就会消耗很多资源的,特别是在动态的散列表中涉及数据的迁移操作,需要重新散列寻找对应的桶位置。其次,我们的散列函数生成的值要保证尽可能的随机并且均匀分布,以避免或者最小化散列冲突。散列函数有很多种设计方式,我们上一篇讲到的就是其中的一种,根据编号的后两位,这样子的设计方法我们称作“数据分析法”。

散列表的装载因子越大,散列表可以容纳的元素就越多,空闲的位置就会越少,造成的散列冲突的概率也就越大。相反的,如果过小的话就会造成计算资源的浪费。所以我们的装载因子也需要考虑一个权衡的值,这是要根据具体的应用场景进行设计的。当散列表达到装载因子的阈值之后,散列表就会触发动态扩容。

总结

设计一个性能优秀的散列表不是一件容易的事情,但是我们的Java语言中就有一个现成的散列表实现类HashMap,可以参考它的设计理念以及设计细节的。