欢迎来到我的频道 【点击跳转专栏】
码云链接 【点此转跳】
文章目录
- [0. 前言](#0. 前言)
- [1. 基本结构](#1. 基本结构)
- [2. 迭代器的实现](#2. 迭代器的实现)
- [3. insert的实现](#3. insert的实现)
- 4.unordered_map的【】实现
- [5. 完整代码](#5. 完整代码)
- [5.1 HashTable.h](#5.1 HashTable.h)
- [5.2 UnorderedMap.h](#5.2 UnorderedMap.h)
- [5.3 UnorderedSet.h](#5.3 UnorderedSet.h)
0. 前言
在阅读本章节前 请保证掌握以下知识 哈希表的实现&&
unorderedmap和unorderedset的使用 同时确保自己熟练掌握map和set的封装(因为里面除了底层一个是红黑树一个是哈希表外 基本区别不大 所以很多冲突部分都不会详细解释 也不会一步一步推导的写 而是围绕成品代码解释为什么这么写)详情可以参考小编写的:
哈希表(C++详解版&&线性探测法、哈希桶的实现)
C++ unordered_map和unordered_set的使用
C++ set&&map的模拟实现
1. 基本结构
跟
map和set相⽐⽽⾔unordered_map和unordered_set的模拟实现类结构更复杂⼀点,但是⼤框架和思路是完全类似的。
HashTable.h(所属文件)
cpp
// ────────────────────────────────────────────────────────
// 改造1:节点不再固定存储 pair<K,V>,而是泛型 T
// 使得同一套哈希表可同时用于 unordered_set<T> 和 unordered_map<K,V>
// ────────────────────────────────────────────────────────
template<class T>
struct HashNode
{
T _data; // 存储实际数据(可能是 K,也可能是 pair<K,V>)
HashNode<T>* _next = nullptr; // 指向下一个冲突节点(单链表)
// 构造函数:用给定数据初始化节点
HashNode(const T& data)
: _data(data)
, _next(nullptr)
{}
};
// ────────────────────────────────────────────────────────
// 默认哈希函数:适用于整型等可直接转换为 size_t 的类型
// ────────────────────────────────────────────────────────
template<class K>
struct HashFunc
{
size_t operator()(const K& key)
{
return (size_t)key; // 强制类型转换(对指针、int 安全)
}
};
// ────────────────────────────────────────────────────────
// 特化版本:针对 std::string 的 BKDR 哈希算法
// 通过累加字符并乘以质数 131,增强散列性,减少相似字符串冲突
// ────────────────────────────────────────────────────────
template<>
struct HashFunc<string>
{
size_t operator()(const string& str)
{
size_t hash = 0;
for (auto ch : str)
{
hash += ch; // 累加 ASCII 值
hash *= 131; // 乘以质数,打乱分布
}
return hash;
}
};
// ────────────────────────────────────────────────────────
// 通用哈希表主类:通过 KeyOfT 提取键,支持 set/map 统一实现
// K: 键类型(用于查找、哈希)
// T: 存储的实际数据类型(set 中为 K,map 中为 pair<K,V>)
// KeyOfT: 仿函数,从 T 中提取 K
// Hash: 哈希函数
// ────────────────────────────────────────────────────────
template<class K, class T, class KeyOfT, class Hash>
class HashTable
{
typedef HashNode<T> Node; // 节点类型别名
public:
// ────────────────────────────────────────────────────
// 构造函数:初始容量设为最小素数(53),避免频繁扩容
// ────────────────────────────────────────────────────
HashTable()
: _tables(__stl_next_prime(0), nullptr)
, _n(0)
{}
// ────────────────────────────────────────────────────
// 析构函数:释放所有动态分配的节点,防止内存泄漏
// ────────────────────────────────────────────────────
~HashTable()
{
for (size_t i = 0; i < _tables.size(); i++)
{
Node* cur = _tables[i];
while (cur)
{
Node* next = cur->_next;
delete cur;
cur = next;
}
_tables[i] = nullptr;
}
}
// ────────────────────────────────────────────────────
// 辅助函数:返回大于等于 n 的最小素数(模仿 SGI STL)
// 用于扩容时选择新容量,素数可使模运算分布更均匀
// ────────────────────────────────────────────────────
inline unsigned long __stl_next_prime(unsigned long n)
{
static const int __stl_num_primes = 28;
static const unsigned long __stl_prime_list[__stl_num_primes] =
{
53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593,
49157, 98317, 196613, 393241, 786433, 1572869, 3145739,
6291469, 12582917, 25165843, 50331653, 100663319,
201326611, 402653189, 805306457, 1610612741,
3221225473UL, 4294967291UL
};
const unsigned long* first = __stl_prime_list;
const unsigned long* last = __stl_prime_list + __stl_num_primes;
const unsigned long* pos = std::lower_bound(first, last, n);
if (pos == last) {
return *(last - 1);
} else {
return *pos;
}
}
private:
vector<Node*> _tables; // 桶数组:每个元素是一个链表头指针
size_t _n; // 实际存储的元素个数(用于控制负载因子)
};
unordered_map.h
cpp
// ────────────────────────────────────────────────────────
// unordered_map:基于 HashTable 实现的无序键值对容器
// K: 键类型,V: 值类型,Hash: 哈希函数(默认使用 HashFunc<K>,用于支持自定义类型转化为唯一的size_t 将关键字转化为整数【不用解释吧我觉得。。 不理解说明对哈希表和unordered_map还是不了解我觉得】)
// 内部通过 MapKeyOfT 从 pair 中提取 key,实现统一接口
// ────────────────────────────────────────────────────────
template<class K, class V, class Hash = HashFunc<K>>
class unordered_map
{
// ────────────────────────────────────────────────────
// 键提取器:用于从存储的数据(pair<const K, V>)中提取 key
// 这是通用 HashTable 能同时支持 set 和 map 的关键机制
// 注意:返回 const K&,防止 key 被修改(key 必须不可变!)
// ────────────────────────────────────────────────────
struct MapKeyOfT
{
const K& operator()(const pair<K, V>& kv)
{
return kv.first; // 返回 pair 的 first 成员(即 key)
}
};
private:
// ────────────────────────────────────────────────────
// 底层哈希表实例:
// - T = pair<const K, V>:确保 key 不可被修改(符合 map 语义)
// - KeyOfT = MapKeyOfT:用于从 pair 中提取 key
// - 所有操作委托给 _t
// ────────────────────────────────────────────────────
HashTable<K, pair<const K, V>, MapKeyOfT, Hash> _t;
};
unordered_set.h(篇幅原因 略 基本一样 最后会放完整代码)
2. 迭代器的实现
哈希表迭代器核心设计要点:
- 整体框架:迭代器封装结点指针,重载运算符模拟指针行为,属于单向迭代器,设计思路与list迭代器一致。
- 核心难点 :
operator++实现------当前桶有结点则指向桶内下一个结点;桶遍历完则通过哈希表对象指针,从当前桶位置往后找首个非空桶。 - 首尾迭代器 :
begin()返回首个非空桶的首个结点迭代器,end()返回空指针构造的迭代器。 - 不可修改性设计 :
unordered_set:哈希表模板参数指定const K,禁止修改元素;unordered_map:哈希表存储pair<const K, V>,仅禁止修改key,value可改。
HashTable.h(所属文件)
cpp
// ────────────────────────────────────────────────────────
// 前置声明:HashTable 类模板(供迭代器友元使用)
// ────────────────────────────────────────────────────────
template<class K, class T, class KeyOfT, class Hash>
class HashTable;
// ────────────────────────────────────────────────────────
// 哈希表迭代器:支持非连续内存的遍历(跳过空桶)
// Ref/Ptr 用于区分普通迭代器和 const 迭代器
// ────────────────────────────────────────────────────────
template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
struct HTIterator
{
typedef HashNode<T> Node;
typedef HashTable<K, T, KeyOfT, Hash> HT;
typedef HTIterator<K, T, Ref, Ptr, KeyOfT, Hash> Self;
Node* _node; // 当前指向的节点
const HT* _ht; // 指向所属哈希表(const 确保不修改表结构)
// 构造函数:初始化当前节点和所属哈希表
HTIterator(Node* node, const HT* ht)
: _node(node)
, _ht(ht)
{}
// 解引用操作符:返回当前节点的数据引用
Ref operator*()
{
return _node->_data;
}
// 成员访问操作符:返回当前节点数据的地址
Ptr operator->()
{
return &_node->_data;
}
// 前置 ++:移动到下一个有效节点
Self& operator++()
{
if (_node->_next)
{
// 同一桶内有下一个节点,直接前进
_node = _node->_next;
}
else
{
// 当前桶已到末尾,需查找下一个非空桶
// 先计算当前节点所在桶索引
size_t hashi = Hash()(KeyOfT()(_node->_data)) % _ht->_tables.size();
++hashi; // 从下一个桶开始找
// 遍历后续桶,直到找到非空桶或越界
while (hashi != _ht->_tables.size())
{
if (_ht->_tables[hashi])
{
_node = _ht->_tables[hashi]; // 指向该桶第一个节点
break;
}
++hashi;
}
// 若所有后续桶都为空,则指向 end()(即 nullptr)
if (hashi == _ht->_tables.size())
{
_node = nullptr;
}
}
return *this;
}
// 不等于比较:仅比较节点指针(end() 为 nullptr)
bool operator!=(const Self& s) const
{
return _node != s._node;
}
// 等于比较
bool operator==(const Self& s) const
{
return _node == s._node;
}
};
template<class K, class T, class KeyOfT, class Hash>
class HashTable
{
// 友元声明:允许对应参数的迭代器访问私有成员
// 注意:此处使用新模板参数 A~F 避免遮蔽外层参数(虽冗余但合法)
template<class A, class B, class C, class D, class E, class F>
friend struct HTIterator;
typedef HashNode<T> Node; // 节点类型别名
public:
// 迭代器类型定义:通过 Ref/Ptr 控制是否可修改
typedef HTIterator<K, T, T&, T*, KeyOfT, Hash> Iterator;
typedef HTIterator<K, T, const T&, const T*, KeyOfT, Hash> ConstIterator;//cosnt版本
// 返回首元素迭代器(第一个非空桶的第一个节点)
Iterator Begin()
{
for (size_t i = 0; i < _tables.size(); i++)
{
if (_tables[i]) // 找到第一个非空桶
{
return Iterator(_tables[i], this);
}
}
return End(); // 全空则返回 end()
}
// 返回尾后迭代器(用 nullptr 表示)
Iterator End()
{
return Iterator(nullptr, this);
}
// const 版本 Begin
ConstIterator Begin() const
{
for (size_t i = 0; i < _tables.size(); i++)
{
if (_tables[i])
{
return ConstIterator(_tables[i], this);
}
}
return End();
}
// const 版本 End
ConstIterator End() const
{
return ConstIterator(nullptr, this);
}
.....
}
⚠️:我觉得唯一的难点应该是Hash()(KeyOfT()(_node->_data)) 的理解了
第 1 层:
_node->_data
_node是一个HashNode<T>*类型的指针;_data是该节点存储的实际数据,类型为T; 若是set,则T = K(如 int); 若是map,则T = pair<const K, V>(如pair<const string, int>)- ✅ 所以:
_node->_data就是当前节点存的完整数据。第 2 层:
KeyOfT()(_node->_data) KeyOfT是一个函数对象类型(仿函数),作为模板参数传入;KeyOfT():临时构造一个KeyOfT类型的对象(无参构造); 然后调用它的operator(),传入_node->_data;- 返回值是 该数据对应的"键(key)",类型为 K。
第 3 层:
Hash()( ... )
- Hash 也是一个函数对象类型(仿函数),默认是
HashFunc<K>;Hash():临时构造一个 Hash 类型的对象; 调用它的operator(),传入上一步提取出的key(类型 K); 返回值是size_t类型的哈希值。
unordered_map.h
cpp
public:
// ────────────────────────────────────────────────────
// 迭代器类型定义:
// - 存储的数据类型为 pair<const K, V>(key 不可变!)
// - 使用 MapKeyOfT 作为键提取策略
// - 与 HashTable 的模板参数严格对齐
// ────────────────────────────────────────────────────
typedef typename HashTable<K, pair<const K, V>, MapKeyOfT, Hash>::Iterator iterator;
typedef typename HashTable<K, pair<const K, V>, MapKeyOfT, Hash>::ConstIterator const_iterator;
// ────────────────────────────────────────────────────
// begin/end:提供标准容器遍历接口
// 调用底层 HashTable 的 Begin/End(注意大小写匹配)
// ────────────────────────────────────────────────────
iterator begin()
{
return _t.Begin(); // 返回第一个有效元素的迭代器
}
iterator end()
{
return _t.End(); // 返回尾后迭代器(nullptr 表示)
}
const_iterator begin() const
{
return _t.Begin();
}
const_iterator end() const
{
return _t.End();
}
unordered_set.h(篇幅原因 略 基本一样 最后会放完整代码)
3. insert的实现
因为
HashTable实现了泛型不知道T参数导致是K,还是pair<K, V>,那么insert内部进⾏插⼊时要⽤K对象转换成整形取模和K⽐较相等 ,因为pair的value不参与计算取模,且默认⽀持的是key和value⼀起⽐较相等,我们需要时的任何时候只需要⽐较K对象,所以我们在unordered_map和unordered_set层分别实现⼀个MapKeyOfT和SetKeyOfT的仿函数传给HashTable的KeyOfT,然后HashTable中通过KeyOfT仿函数取出T类型对象中的K对象,再转换成整形取模和K⽐较相等,具体细节参考如下代码实现。
HashTable.h(所属文件)
cpp
// ────────────────────────────────────────────────────
// 插入操作:返回 pair<Iterator, bool>
// bool 表示是否插入成功,Iterator 指向(新/已有)元素
// ────────────────────────────────────────────────────
pair<Iterator, bool> Insert(const T& data)
{
KeyOfT kot; // 创建键提取器
auto it = Find(kot(data)); // 查重
if (it != End())
{
return { it, false }; // 已存在,返回已有位置
}
Hash hs; // 创建哈希函数对象
// 扩容条件:负载因子 = 1(平均链长=1)
if (_n == _tables.size())
{
// 创建新桶数组(素数容量)
vector<Node*> newtables(__stl_next_prime(_tables.size() + 1));
// 原地迁移节点:复用旧节点,避免 new/delete
for (size_t i = 0; i < _tables.size(); i++)
{
Node* cur = _tables[i];
while (cur)
{
Node* next = cur->_next;
// 重新计算在新表中的桶索引
size_t hashi = hs(kot(cur->_data)) % newtables.size();
// 头插到新桶
cur->_next = newtables[hashi];
newtables[hashi] = cur;
cur = next;
}
_tables[i] = nullptr; // 清空旧桶,防 double-free
}
_tables.swap(newtables); // 高效交换
}
// 计算新数据应插入的桶
size_t hashi = hs(kot(data)) % _tables.size();
Node* newNode = new Node(data);
newNode->_next = _tables[hashi];
_tables[hashi] = newNode;
++_n;
// 返回指向新节点的迭代器
return { Iterator(newNode, this), true };
}
unordered_map.h
cpp
// ────────────────────────────────────────────────────
// insert:插入键值对
// 返回 pair<iterator, bool>:
// - bool: true 表示插入成功,false 表示 key 已存在
// - iterator: 指向(新插入或已存在的)元素
// 注意:传入的是 pair<K,V>,但底层存储为 pair<const K,V>
// C++ 会自动转换(因为 const K 可由 K 初始化)
// ────────────────────────────────────────────────────
pair<iterator, bool> insert(const pair<K, V>& kv)
{
return _t.Insert(kv); // 转发到底层哈希表
}
4.unordered_map的【】实现
unordered_map要⽀持[]主要需要修改insert返回值⽀持,修改HashTable中的insert返回值为pair<Iterator, bool> Insert(const T& data)(至于为什么设计到了STL的底层 我前面是修改好了的 至于为什么可以转跳我写的 map的使用 里面有详细的解释哦~)
unordered_map.h
cpp
// ────────────────────────────────────────────────────
// operator[]:下标访问操作符(核心特性!)
// 行为:
// - 若 key 存在,返回对应 value 的引用;
// - 若 key 不存在,插入 {key, V()}(值初始化),并返回新 value 的引用;
// 实现依赖 insert 的返回值(iterator 指向 pair)
// 注意:V() 是值初始化(如 int→0, string→"")
// ────────────────────────────────────────────────────
V& operator[](const K& key)
{
// 尝试插入 {key, 默认构造的 V}
pair<iterator, bool> ret = insert({key, V()});
// 无论是否新插入,ret.first 都指向该 key 对应的 pair
// 返回 pair 的 second(即 value 的引用)
return ret.first->second;
}
5. 完整代码
提一句 大家可以按照以下顺序尝试自己实现(博主就是这么做的 记住 一口吃不成胖子)

5.1 HashTable.h
cpp
//
// HashTable.h
// 哈希表
//
// Created by Fanz on 2026/1/8.
//
#pragma once
//改造1
// template<class K,class V>
template<class T>
struct HashNode
{
// pair<K, V> _kv;
// HashNode<K, V>* _next=nullptr;
T _data;
HashNode<T>* _next=nullptr;
HashNode(const T& data)
:_data(data)
,_next(nullptr)
{}
};
template<class K>
struct HashFunc
{
size_t operator()(const K& key)
{
return (size_t)key;
}
};
//特化
template<>
struct HashFunc<string>
{
// BKDR
size_t operator()(const string& str)
{
size_t hash = 0;
for (auto ch : str)
{
hash += ch;
hash *= 131;//BKDR哈希 一种优化字符串哈希映射的方法 尽量避免相似字符串冲突问题
}
return hash;
}
};
template<class K,class T,class KeyOfT,class Hash>
class HashTable;
template<class K,class T,class Ref,class Ptr,class KeyOfT,class Hash>
struct HTIterator
{
typedef HashNode<T> Node;
typedef HashTable<K, T, KeyOfT,Hash> HT;
typedef HTIterator<K, T,Ref,Ptr,KeyOfT,Hash> Self;
Node* _node;
// HT* _ht;
//重点!!!
const HT* _ht;
HTIterator(Node* node,const HT* ht)
:_node(node)
,_ht(ht)
{}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
Self& operator++()
{
if (_node->_next)
{
_node=_node->_next;
}
else //当前桶为空,找下一个不为空的桶的第一个
{
size_t hashi = Hash()(KeyOfT()(_node->_data))% _ht->_tables.size();
++hashi;
while (hashi!= _ht->_tables.size())
{
if(_ht->_tables[hashi])
{
_node = _ht->_tables[hashi];
break;
}
++hashi;
}
//最后一个桶的最后一个节点已经遍历结束,走到end()去,nullptr充当end()
if (hashi==_ht->_tables.size())
{
_node =nullptr;
}
}
return *this;
}
bool operator!=(const Self& s) const
{
return _node!=s._node;
}
bool operator==(const Self& s) const
{
return _node == s._node;
}
};
//第二个模版参数T 来控制底层
template<class K,class T,class KeyOfT,class Hash>
class HashTable
{
template<class A,class B,class C,class D,class E,class F>
//友元申明
friend struct HTIterator;
typedef HashNode<T> Node;
public:
typedef HTIterator<K, T,T&,T* ,KeyOfT,Hash> Iterator;
typedef HTIterator<K,T,const T&, const T*, KeyOfT,Hash> ConstIterator;
Iterator Begin()
{
for (size_t i= 0; i<_tables.size(); i++)
{
if (_tables[i])
{
return Iterator(_tables[i],this);
}
}
return End();
}
Iterator End()
{
return Iterator(nullptr,this);
}
ConstIterator Begin() const
{
for (size_t i= 0; i<_tables.size(); i++)
{
if (_tables[i])
{
return ConstIterator(_tables[i],this);
}
}
return End();
}
ConstIterator End() const
{
return ConstIterator(nullptr,this);
}
HashTable()
:_tables(__stl_next_prime(0),nullptr)
,_n(0)
{}
~HashTable()
{
for (size_t i =0; i< _tables.size(); i++)
{
Node* cur = _tables[i];
while (cur)
{
Node* next = cur->_next;
delete cur;
cur = next;
}
_tables[i] =nullptr;
}
}
// 返回大于等于 n 的最小素数(质数)
// 该函数用于哈希表扩容时选择合适的桶数量,以减少哈希冲突(素数长度可使模运算分布更均匀)
inline unsigned long __stl_next_prime(unsigned long n)
{
// 假设:unsigned long 至少为 32 位(现代系统通常满足,64 位系统也兼容)
// 预定义的素数表长度(共 28 个精心挑选的质数)
static const int __stl_num_primes = 28;
// 静态常量数组:一组递增的质数,覆盖从几十到接近 2^32 的范围
// 这些质数满足:
// - 每个都 ≈ 前一个的 2 倍(便于哈希表按"约两倍"扩容)
// - 都是质数(避免与常见 key 的倍数产生周期性冲突)
// - 最后一个 (4294967291) 是小于 2^32 的最大质数之一(适用于 32 位地址空间)
static const unsigned long __stl_prime_list[__stl_num_primes] =
{
53, // 初始小容量(> 32,避免频繁扩容)
97, // ≈ 53 * 1.8
193, // ≈ 97 * 2
389,
769,
1543,
3079,
6151,
12289,
24593,
49157,
98317,
196613,
393241,
786433,
1572869,
3145739,
6291469,
12582917,
25165843,
50331653,
100663319,
201326611,
402653189,
805306457,
1610612741,
3221225473UL, // 注意:超过 2^31,需标记为 unsigned
4294967291UL // = 2^32 - 61,是 < 2^32 的最大质数之一
};
// 定义数组的起始和结束指针(用于标准算法)
const unsigned long* first = __stl_prime_list; // 指向第一个元素
const unsigned long* last = __stl_prime_list + __stl_num_primes; // 指向末尾之后
// 使用二分查找(std::lower_bound)找到第一个 >= n 的素数
// lower_bound 返回指向首个不小于 n 的元素的指针
const unsigned long* pos = std::lower_bound(first, last, n);
// 边界处理(越界会返回last):
if (pos == last) {
// 如果 n 大于所有预定义素数(比如 n > 4294967291)
// 则返回最大的可用素数(防止越界)
return *(last - 1);
} else {
// 否则返回找到的素数(>= n 的最小质数)
return *pos;
}
}
pair<Iterator, bool> Insert(const T& data)
{
KeyOfT kot;
//去冗余
auto it =Find(kot(data));
if (it!=End())
{
return {it,false};
}
Hash hs;
if (_n==_tables.size())
{
vector<Node*> newtables(__stl_next_prime(_tables.size()+1));
for (size_t i=0; i<_tables.size(); i++)
{
Node* cur = _tables[i];
while (cur)
{
Node* next = cur->_next;
//插入到新表
size_t hashi = hs(kot(cur->_data))%newtables.size();
cur->_next = newtables[hashi];
newtables[hashi]=cur;
cur = next;
}
_tables[i]=nullptr;
}
_tables.swap(newtables);
}
//size_t hashi = hs(kv.first) % _tables.size();
size_t hashi = hs(kot(data)) % _tables.size();
//头插
Node* newNode = new Node(data);
newNode->_next = _tables[hashi];
_tables[hashi]=newNode;
++_n;
// return true;
return {Iterator(newNode,this),true};
}
Iterator Find(const K& key)
{
KeyOfT kot;
Hash hs;
size_t hashi = hs(key) % _tables.size();
Node* cur = _tables[hashi];
while (cur)
{
if (kot(cur->_data) == key)
{
return {cur,this};
}
cur = cur -> _next;
}
return End();
}
bool Erase(const K& key)
{
KeyOfT kot;
Hash hs;
size_t hashi = hs(key) % _tables.size();
Node* prev =nullptr;
Node* cur = _tables[hashi];
while (cur)
{
if (kot(cur->_data) == key)
{
if (prev == nullptr)
{
_tables[hashi] = cur->_next;
}
else
{
prev->_next=cur->_next;
}
delete cur;
--_n;
return true;
}
prev = cur;
cur = cur->_next;
}
return false;
}
private:
vector<Node*> _tables;
size_t _n; //实际存储的数据个数
};
5.2 UnorderedMap.h
cpp
//
// UnorderedMap.h
// 用哈希表封装myunordered_map和 myunordered_set
//
// Created by Fanz on 2026/1/21.
//
#pragma once
#include"HashTable.h"
namespace fcy
{
template<class K,class V,class Hash = HashFunc<K>>
class unordered_map
{
struct MapKeyOfT
{
const K& operator()(const pair<K,V>& kv)
{
return kv.first;
}
};
public:
typedef typename HashTable<K,pair<const K,V>,MapKeyOfT,Hash>::Iterator iterator;
typedef typename HashTable<K,pair<const K,V>,MapKeyOfT,Hash>::ConstIterator const_iterator;
iterator begin()
{
return _t.Begin();
}
iterator end()
{
return _t.End();
}
const_iterator begin() const
{
return _t.Begin();
}
const_iterator end() const
{
return _t.End();
}
pair<iterator, bool> insert(const pair<K,V>& kv)
{
return _t.Insert(kv);
}
V& operator[](const K& key)
{
pair<iterator,bool> ret = insert({key,V()});
return ret.first->second;
}
private:
HashTable<K,pair<const K,V>,MapKeyOfT,Hash>_t;
};
}
5.3 UnorderedSet.h
cpp
//
// UnorderedSet.h
// 用哈希表封装myunordered_map和 myunordered_set
//
// Created by Fanz on 2026/1/21.
//
#pragma once
#include"HashTable.h"
namespace fcy
{
template<class K,class Hash = HashFunc<K>>
class unordered_set
{
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
public:
typedef typename HashTable<K, const K,SetKeyOfT,Hash>::Iterator iterator;
typedef typename HashTable<K,const K,SetKeyOfT,Hash>::ConstIterator const_iterator;
iterator begin()
{
return _t.Begin();
}
iterator end()
{
return _t.End();
}
const_iterator begin() const
{
return _t.Begin();
}
const_iterator end() const
{
return _t.End();
}
pair<iterator, bool> insert(const K& k)
{
return _t.Insert(k);
}
private:
HashTable<K,const K,SetKeyOfT,Hash> _t; //第二个模版参数决定存什么
};
}
