当前位置: 首页 > news >正文

C++ 手写实现 unordered_map 和 unordered_set:深入解析与源码实战

STL 哈希容器的演进
2.1 SGI STL 与 C++11

早期的 SGI STL 中并没有unordered_mapunordered_set,而是提供了hash_maphash_set作为扩展容器。它们并不属于 C++ 标准委员会制定的 STL 标准库,而是由 SGI 组织实现并广泛流传。

随着 C++11 的到来,unordered_mapunordered_set正式成为标准容器,被纳入<unordered_map><unordered_set>头文件中。

2.2hash_mapunordered_map对比

虽然名字不同,但结构设计和使用方式高度类似,都依赖底层的哈希表实现。不过由于unordered_map是 C++11 正式标准的一部分,因此更具可移植性和规范性。


三、SGI STL 哈希容器源码结构分析

SGI STL 的hash_maphash_set都基于通用模板类hashtable实现,示例如下:

代码语言:javascript

AI代码解释

// hash_set class hash_set { typedef hashtable<Value, Value, HashFcn, identity<Value>, EqualKey, Alloc> ht; ht rep; }; // hash_map class hash_map { typedef hashtable<pair<const Key, T>, Key, HashFcn, select1st<pair<const Key, T>>, EqualKey, Alloc> ht; ht rep; };

底层哈希表结构:

代码语言:javascript

AI代码解释

struct __hashtable_node { __hashtable_node* next; Value val; }; class hashtable { vector<node*> buckets; size_t num_elements; };

SGI STL 使用链表处理冲突,即“拉链法”。


四、从零构建哈希表框架
4.1 哈希节点结构

代码语言:javascript

AI代码解释

template<class T> struct HashNode { T _data; HashNode<T>* _next; HashNode(const T& data) : _data(data), _next(nullptr) {} };
4.2 哈希函数定义

代码语言:javascript

AI代码解释

template<class K> struct HashFunc { size_t operator()(const K& key) const { return static_cast<size_t>(key); } }; // 特化版本支持 string template<> struct HashFunc<std::string> { size_t operator()(const std::string& key) const { size_t hash = 0; for (char ch : key) hash = hash * 131 + ch; return hash; } };
4.3 哈希表接口结构

代码语言:javascript

AI代码解释

template<class K, class T, class KeyOfT, class Hash> class HashTable { vector<HashNode<T>*> _tables; size_t _n; public: bool Insert(const T& data); Iterator Begin(); Iterator End(); };

五、实现插入逻辑与扩容机制

插入的基本步骤如下:

  • 用哈希函数求出索引
  • 查找是否已存在该 key
  • 若不存在则头插法插入
  • 若负载因子达到阈值,执行扩容

扩容逻辑采用“下一个质数原则”,以尽可能减少冲突。


六、封装 unordered_set 与 unordered_map

代码语言:javascript

AI代码解释

namespace bit { template<class K, class Hash = HashFunc<K>> class unordered_set { struct SetKeyOfT { const K& operator()(const K& key) const { return key; } }; hash_bucket::HashTable<K, K, SetKeyOfT, Hash> _ht; public: bool insert(const K& key) { return _ht.Insert(key); } }; }

代码语言:javascript

AI代码解释

namespace bit { template<class K, class V, class Hash = HashFunc<K>> class unordered_map { struct MapKeyOfT { const K& operator()(const pair<K, V>& kv) const { return kv.first; } }; hash_bucket::HashTable<K, pair<K, V>, MapKeyOfT, Hash> _ht; public: bool insert(const pair<K, V>& kv) { return _ht.Insert(kv); } V& operator[](const K& key) { pair<K, V> kv(key, V()); auto ret = _ht.Insert(kv); return const_cast<V&>(ret.first->_data.second); } }; }

七、自定义迭代器的设计与实现

unordered_*的迭代器是单向的(ForwardIterator)。其逻辑包括:

  • 遍历链表节点_next
  • 若当前桶走完,跳到下一个非空桶

定义如下:

代码语言:javascript

AI代码解释

template<class K, class T, class Ptr, class Ref, class KeyOfT, class Hash> struct HTIterator { Node* _node; const HashTable<K, T, KeyOfT, Hash>* _pht; HTIterator(Node* node, const HashTable<K, T, KeyOfT, Hash>* pht); Ref operator*() const; Ptr operator->() const; Self& operator++(); bool operator!=(const Self& s) const; };

配合Begin()End()实现遍历:

代码语言:javascript

AI代码解释

for (auto it = s.begin(); it != s.end(); ++it) cout << *it << endl;

八、operator[] 的支持与 Key 不可修改约束
8.1 为什么 key 不可修改?

哈希表中 key 决定了数据的桶位置,如果允许修改 key,可能导致哈希结构紊乱。因此标准库中使用pair<const K, V>

8.2 operator[] 的实现逻辑

代码语言:javascript

AI代码解释

V& operator[](const K& key) { pair<K, V> kv(key, V()); auto ret = _ht.Insert(kv); return const_cast<V&>(ret.first->_data.second); }
8.3 对比 insert 与 []

特性

insert(kv)

operator[]

功能

插入(存在返回 false)

查找或插入

返回值

pair<it, bool>

value 引用

修改 key

修改 value

需要迭代器访问 second

支持


九、扩容机制与性能调优建议
9.1 扩容策略:质数数组

采用质数作为哈希桶数量有助于减少哈希冲突。

代码语言:javascript

AI代码解释

static const size_t prime_list[] = { 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593, 49157, 98317, 196613 };
9.2 负载因子 (load factor)

常设阈值为 1.0,当元素个数达到桶总数时扩容,可动态调整:

代码语言:javascript

AI代码解释

if (_n >= _tables.size()) Expand();
9.3 哈希冲突优化
  • 自定义哈希函数(如 BKDRHash)
  • 增加桶数量,减少冲突链长度
  • 使用链表以外的冲突解决(如开放定址)

十、总结

本博客从 STL 哈希容器的历史出发,逐步带你实现unordered_mapunordered_set,涵盖了:

  • 哈希表的构建逻辑
  • 模板泛型与仿函数提取 key
  • insert、[] 和迭代器的完整支持
  • key 不可修改原则与性能优化建议
http://www.jsqmd.com/news/454467/

相关文章:

  • 【Linux系统】进程状态 | 进程优先级
  • 中小企业布局信创实时云渲染,可行吗?
  • C++ 定长内存池,让内存分配快到飞起!
  • 信创实时云渲染与传统本地渲染,企业选型该瞄准哪些核心点?
  • 【毕业设计】SpringBoot+Vue+MySQL 医院信管系统平台源码+数据库+论文+部署文档
  • SpringBoot+Vue 智能菜谱推荐系统管理平台源码【适合毕设/课设/学习】Java+MySQL
  • C++ 异常处理机制详解:从基础语法到工程实践
  • 2026年江苏变压器铜铝排/变压器铜电磁线/变压器铝电磁线服务商采购白皮书:高压输配电领域的核心供应商竞争力解析 - 2026年企业推荐榜
  • Flutter 三方库 ntp_dart 的鸿蒙化适配指南 - 获取绝对可信的授时服务、助力鸿蒙端金融与考勤类应用杜绝本地时钟作弊风险
  • 【Linux系统】理解硬件 | 引入文件系统
  • 《Linux 输入输出重定向与 VI 编辑器:全面操作指南与原理剖析》
  • Spring推出Spring AI框架,看看怎么个事
  • 2026年无纸化会议系统推荐指南:会议音响套装/吸顶会议音箱/国产无纸化会议/多媒体室音响/大礼堂音响/选择指南 - 优质品牌商家
  • 【Linux系统】进程地址空间
  • Linux网络编程:应用层自定义协议与序列化
  • 2026年外贸建站公司实力大盘点:口碑、技术、信用TOP级企业全解析 - 品牌推荐大师1
  • 年度总结:我的技术成长与反思
  • 【Linux系统】命令行参数和环境变量
  • 核“芯”动力,重构无人机通信边界——LR1121IMLTRT 多频段LoRa收发器
  • Java项目中策略模式的使用方法:从零开始掌握可扩展业务逻辑设计
  • 互联网大厂Java小白面试:从基础到进阶的技术问答细节
  • 2026年快速温变试验箱优质供应商盘点:哪家能耗更低? - 品牌推荐大师
  • 2026年波纹金属软管厂商评价排行,目前评价好的波纹金属软管厂商选哪家,波纹补偿器/阀用波纹管,波纹金属软管品牌推荐 - 品牌推荐师
  • 零碳园区商业模式创新的政策支持对企业有哪些影响?
  • Linux服务器崩溃急救指南:实战演练常见故障排查
  • 互联网大厂Java面试:Spring Boot微服务与Redis缓存应用场景分析
  • Flutter 三方库 clean_feature_gen 的鸿蒙化适配指南 - 掌握整洁架构自动化生成技术、助力大中型项目构建高内聚、低耦合且极速迭代的功能模块体系
  • Java Web 榆林特色旅游网站系统源码-SpringBoot2+Vue3+MyBatis-Plus+MySQL8.0【含文档】
  • 柴油发电机3D模型图纸 Solidworks设计
  • 2026热收缩膜包装机优质厂商推荐榜 - 优质品牌商家