Map、Dictionary、Hash Table:到底该用哪一个?
开场白
面试常问,代码常写,Google 常搜。
“Map、Dictionary、Hash Table 有啥区别?”
今天用一篇博客讲透:它们分别活在哪一层、各有什么超能力,以及——真正写代码时怎么选。
1. Hash Table(哈希表)
1.1 定义
最底层的数据结构:数组 + Hash 函数 + 冲突解决策略(Separate Chaining / Open Addressing / Robin-Hood…)。
1.2 超能力
均摊时间复杂度 O(1):增、删、查。
内存紧凑:指针可控、Cache-Friendly。
可高度定制:开放地址省指针,Robin-Hood 省 CPU Cache Miss。
1.3 适用场景
写底层库:Redis dict、Linux 路由表、数据库索引。
性能极限场景:游戏引擎 ECS、高频交易系统撮合引擎。
一句话:当你需要“把哈希函数握在自己手里”时,选 Hash Table。
2. Dictionary(字典)
2.1 定义
各大语言标准库对“基于 Hash Table 的键值容器”的统称:
C#
Dictionary<TKey,TValue>
Python
dict
Swift
Dictionary<Key, Value>
2.2 超能力
开箱即用:
get / set / remove / ContainsKey
一套带走。键类型 = 可哈希且可相等(string、number、enum…)。
无序:语义上不保证遍历顺序(Python 3.7+ 只是实现细节)。
2.3 适用场景
99% 的业务代码都适合:
根据
userId
找用户根据枚举值拿配置
缓存序列化结果、计数器
一句话:不想造轮子,只想把键值塞进一个黑盒,用 Dictionary。
3. Map(映射)
3.1 定义
ES6 / Java / TypeScript 中的“高配”键值容器。
3.2 超能力
键可以是任意类型:对象、Symbol、函数、NaN。
记住插入顺序:天然可迭代,
for…of
顺序稳定。size 直读:不用
Object.keys(obj).length
。弱引用版本:
WeakMap
防内存泄漏。
3.3 适用场景
键必须是 DOM 节点 / 对象 / Symbol(事件总线、元数据挂载)。
需要 稳定顺序(LRU Cache、插件加载顺序)。
高频增删且不想踩
__proto__
坑。
一句话:当 Dictionary 的键类型或顺序限制让你抓狂,上 Map。
4. 横向对比表(收藏级)
表格
复制
维度 | Hash Table | Dictionary | Map |
---|---|---|---|
抽象层级 | 数据结构裸实现 | 标准库封装 | 标准库高配封装 |
键类型限制 | 自己定 | 可哈希标量 | 任意类型 |
插入顺序 | 无保证 | 无保证 | 保留 |
时间复杂度 | O(1) 均摊 | O(1) 均摊 | O(1) 均摊(略慢,可忽略) |
API 丰富度 | 0(自己写) | 中等 | 高(迭代器、WeakMap) |
业务代码开闭原则 | ❌ 开 | ✅ 闭 | ✅ 闭 |
典型用途 | 写底层、性能极限 | 99% 业务键值存储 | 对象键 / 顺序敏感 |
5. 一句话选型口诀(背下来)
键是字符串 / 数字?Dictionary。
键是对象 / Symbol / 要顺序?Map。
需要魔改哈希函数、榨干最后一滴性能?Hash Table。
6. 彩蛋:代码片段对照
// Dictionary 写法(TypeScript)
const users: Record<string, User> = {};// Map 写法(TypeScript)
const usersMap = new Map<string, User>();// 自己撸 Hash Table(C 伪代码)
struct Entry { char* key; void* val; };
Entry table[1024];
uint32_t hash(char* k) { /* djb2 */ }
如果这篇博客帮你在面试或 CR 中少吵一次架,点个 👍 再走吧!