哈希表:为什么它找东西快到不讲理
把 key 亲手塞进桶里,看哈希函数怎么把「翻一遍」变成「直达」;再看两个 key 撞进同一个桶时怎么收场
本页解决的问题
先给结论「哈希表:为什么它找东西快到不讲理」要解决的关键问题是什么?
把 key 亲手塞进桶里,看哈希函数怎么把「翻一遍」变成「直达」;再看两个 key 撞进同一个桶时怎么收场
让这个结论先证明自己值得留下。 把这一页当成决策工具,而不是需要背下来的定义。把概念连到一个真实任务、一个可观察结果,以及一个能改变你判断的失败上。
写下一个问题:试完这个方法后,你能用什么证据回答它?
结论听起来很完整,却没有检查最关键的假设。
回到收纳的比喻:大抽屉找东西要挨个翻,是因为你不知道东西在哪。哈希表的思路彻底反过来——放进去的那一刻,就用一条固定的公式算出它该放在几号桶;要找的时候,用同一条公式再算一遍,直接开那个桶。这条公式就叫哈希函数,它是一张「定位公式」:不用翻,一步算出在哪。
下面是 8 个编号 0 到 7 的桶,和 6 个等着入住的名字。点一个名字,看它怎么三步进桶:先把每个字变成电脑里的编码数字并求和,再对 8 求余(因为只有 8 个桶),最后飞进算出来的那个桶。留意:全程没有「挨个对比」这个动作,位置完全是算出来的。
你可能已经发现了:阿芳和丽丽算出来都是 2 号桶!这叫哈希碰撞——桶就 8 个,名字千千万,撞桶迟早发生。怎么办?最常用的办法朴素得可爱:在桶里挂一条小链,后来的排在链上(术语叫「链地址法」)。按顺序点下面三个按钮,留意查找时翻了几次。
现在把数据量拉大,让两种找法正面赛跑。选一个数据量,点开跑。留意右边的计数器:不管左边翻到天荒地老,它永远停在 1-2 次。
🗄 翻一遍(线性查找)
翻找 0 次🗃 直达(哈希查找)
翻找 0 次哈希表可能是你每天被服务次数最多的结构——只是它总躲在幕后。下面四个场景,背后全是同一招「算出位置,一步直达」。
Set 与字典
第一课版本 B 的 Set、Python 的 dict、JS 的 Map——语言里所有「按 key 取值」的容器,肚子里都是哈希表。
缓存的键
缓存要在毫秒内回答「这个问题算过吗」,靠的就是把问题哈希成 key 直达查询——下一课的主角。
去重
训练语料去重、爬虫判断「这个网页抓过没」,都是把内容哈希后进 Set 一查——不然亿级数据两两对比要算到宇宙热寂。
session 查找
你每次打开 ChatGPT,服务器拿着 session id 在千万在线用户里瞬间找到你的会话——靠的不是翻名单。
「揭底时刻 · 它不翻,它靠算」为什么要看操作
「回到收纳的比喻:大抽屉找东西要挨个翻,是因为你 不知道东西在哪 。哈希表的思路彻底反过来——放进去的那一刻,就用一条固定的公式 算出它该放在几号桶 ;要找的时候,用同一条公式再算一遍,直接开那个桶。这条公式就叫 哈希函数 ,它是一张「定位公式」:不用翻,一步算出在哪」把结构落到了一个具体动作。这里真正要比较的不是名词谁更高级,而是数据如何被放置,以及最常发生的操作需要走多远。
读懂结构,要同时看访问方式和变化方式
「下面是 8 个编号 0 到 7 的桶,和 6 个等着入住的名字。」揭示了一个容易被忽略的取舍:按位置读取、按键查找、从两端进出、插入新元素和遍历关系,适合的组织方式并不相同。一个结构在某个操作上很快,不代表它在所有操作上都快。
- 哈希函数 = 定位公式 :放和找用同一条公式,位置是算出来的,不是翻出来的
- 耗时和数据量无关 :6 个人算一次,600 万人还是算一次——这就是版本 B「直达」的真相
- 碰撞不可怕 :撞桶就在桶里挂条小链;链太长就加桶重排(扩容)
把规模和更新频率一起算进去
实践时可以把「你每次打开 ChatGPT,服务器拿着 session id 在千万在线用户里 瞬间 找到你的会话——靠的不是翻名单」当作边界提醒:先写下数据量、最常用的操作和允许的延迟,再看 AI 给出的结构是否真的匹配。
从「揭底时刻 · 它不翻,它靠算」走到「动手玩 · 亲手把 key 塞进桶」
「揭底时刻 · 它不翻,它靠算」先把问题落在「回到收纳的比喻:大抽屉找东西要挨个翻,是因为你 不知道东西在哪 。哈希表的思路彻底反过来——放进去的那一刻,就用一条固定的公式 算出它该放在几号桶 ;要找的时候,用同一条公式再算一遍,直接开那个桶。这条公式就叫 哈希函数 ,它是一张「定位公式」:不用翻,一步算出在哪」上;到了「动手玩 · 亲手把 key 塞进桶」,讨论继续推进到「下面是 8 个编号 0 到 7 的桶,和 6 个等着入住的名字。 点一个名字 ,看它怎么三步进桶:先把每个字变成电脑里的编码数字并求和,再对 8 求余(因为只有 8 个桶),最后飞进算出来的那个桶。 留意 :全程没有「挨个对比」这个动作,位置完全是算出来的」。两段连起来,重点就不只是记住一个结论,而是看清它成立所依赖的条件。
把这条判断带到下一个场景
遇到一个新的数据结构时,不要从定义开始背。先写出最频繁的操作,再估计数据量和更新方式,最后检查结构是否让这三个条件同时成立。
- 「揭底时刻 · 它不翻,它靠算」:回到收纳的比喻:大抽屉找东西要挨个翻,是因为你 不知道东西在哪 。哈希表的思路彻底反过来——放进去的那一刻,就用一条固定的公式 算出它该放在几号桶 ;要找的时候,用同一条公式再算一遍,直接开那个桶。这条公式就叫 哈希函数 ,它是一张「定位公式」:不用翻,一步算出在哪
- 「动手玩 · 亲手把 key 塞进桶」:下面是 8 个编号 0 到 7 的桶,和 6 个等着入住的名字。 点一个名字 ,看它怎么三步进桶:先把每个字变成电脑里的编码数字并求和,再对 8 求余(因为只有 8 个桶),最后飞进算出来的那个桶。 留意 :全程没有「挨个对比」这个动作,位置完全是算出来的
- 「最后的要点」:验收视角 :看到「在大名单里挨个找」的代码,就该问一句「这里为什么不用哈希?」
最后的「最后的要点」把讨论落到「验收视角 :看到「在大名单里挨个找」的代码,就该问一句「这里为什么不用哈希?」」。回看这条线索时,最值得保留的是:当输入、规模或风险改变,哪些判断需要重新做一遍。
✅ 这一课想和你分享的
- 哈希函数 = 定位公式:放和找用同一条公式,位置是算出来的,不是翻出来的
- 耗时和数据量无关:6 个人算一次,600 万人还是算一次——这就是版本 B「直达」的真相
- 碰撞不可怕:撞桶就在桶里挂条小链;链太长就加桶重排(扩容)
- 空间换时间:多备一套「定位公式 + 桶」,换来查询自由——AI 世界里 Set、字典、缓存键、去重、session 全是它
- 验收视角:看到「在大名单里挨个找」的代码,就该问一句「这里为什么不用哈希?」
我把这篇文章里的一个判断改写成了今天可以验证的小实验。比记住结论更有用的是,知道下一步要观察什么。
读完以后我先回头找它成立的条件,而不是直接把方法搬进项目。这个顺序让后面的取舍清楚很多。
如果把这个判断放到真实工作里,最先需要补的约束是什么?我想知道从阅读到第一次实践之间,哪一步最值得先做。
还没有这篇文章的讨论。