⽤哈希表封装unordered_map和unordered_set(C++模拟实现)

欢迎来到我的频道 【点击跳转专栏】

码云链接 【点此转跳】

文章目录

  • [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. 迭代器的实现

哈希表迭代器核心设计要点:

  1. 整体框架:迭代器封装结点指针,重载运算符模拟指针行为,属于单向迭代器,设计思路与list迭代器一致。
  2. 核心难点operator++实现------当前桶有结点则指向桶内下一个结点;桶遍历完则通过哈希表对象指针,从当前桶位置往后找首个非空桶。
  3. 首尾迭代器begin()返回首个非空桶的首个结点迭代器,end()返回空指针构造的迭代器。
  4. 不可修改性设计
    • 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不参与计算取模,且默认⽀持的是keyvalue⼀起⽐较相等,我们需要时的任何时候只需要⽐较K对象,所以我们在unordered_map和unordered_set层分别实现⼀个MapKeyOfTSetKeyOfT的仿函数传给HashTableKeyOfT,然后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; //第二个模版参数决定存什么
};

}
相关推荐
CSDN_RTKLIB3 小时前
右值引用一个误区
c++
一分之二~4 小时前
二叉树--层序遍历(迭代和递归)
数据结构·c++·算法·leetcode
zhooyu4 小时前
OpenGL 与 C++:深入理解与实现 Transform 组件
开发语言·c++
东方轧线5 小时前
突破锁竞争的性能枷锁:深度剖析 C++ 内存模型与无锁编程在超大规模并行 AI 系统中的极致应用实践
java·c++·人工智能
Word码5 小时前
[C++语法]-string类(用法详解及实现)
开发语言·c++
CSDN_RTKLIB5 小时前
临时对象产生与值类别范畴
c++
CSDN_RTKLIB5 小时前
std::move 详细介绍
c++
散峰而望6 小时前
【基础算法】高精度运算深度解析与优化
数据结构·c++·算法·链表·贪心算法·推荐算法
彩妙不是菜喵6 小时前
STL精讲:string类
开发语言·c++