C++中用哈希表封装myunordered_set和myunordered_map方法详解

 更新时间:2026年05月05日 09:23:34   作者:进击的荆棘  
这篇文章主要介绍了C++中用哈希表封装myunordered_set和myunordered_map方法,封装通用哈希表底层,快速实现myunordered_set和myunordered_map,掌握STL无序容器核心设计,需要的朋友可以参考下

1.源码及框架分析

SGI-STL30版本源代码中没有unordered_map和unordered_set,SGI-STL30版本是C++11之前的STL版本,这两个容器是C++11之后才更新的。但是SGI-STL30实现了哈希表,只是容器的名字是hash_map和hash_set,它是作为非标准容器出现的,非标准是指非C++标准规定必须实现的,源代码在hash_map/hash_set/set_hash_map/stl_hash_set/stl_hashtable.h中

hash_map和hash_set的实现结构框架核心部分截取:

//stl_hash_set
template<class Value,class HashFcn=hash<Value>,
            class EqualKey=equal_to<Value>,
            class Alloc=alloc>
class hash_set{
private:
    typedef hashtable<Value,Value,HashFcn,identity<Value>,
                                EqualKey,Alloc> ht;
    ht rep;
public:
    typedef typename ht::key_type key_type;
    typedef typename ht::value_type value_type;
    typedef typename ht::hasher hasher;
    typedef typename ht::key_equal key_equal;
    typedef typename ht::const_iterator iterator;
    typedef typename ht::const_iterator const_iterator;
    hasher hash_funct() const {return rep.hash_funct();}
    key_equal key_eq() const {return rep.key_eq();}
};
//stl_hash_map
template<class Key,class T,class HashFcn=hash<Key>,
                    class EqualKey=equal_to<Key>,
                    class Alloc=alloc>
class hash_map{
private:
    typedef hashtable<pair<const Key,T>,Key,HashFcn,
                        selectlst<pair<const Key,T>>,EqualKey,Alloc> ht;
    ht rep;
public:
    typedef typename ht::key_type key_type;
    typedef T data_type;
    typedef T mapped_type;
    typedef typename ht::value_type value_type;
    typedef typename ht::hasher hasher;
    typedef typename ht::key_equal key_equal;
    typedef typename ht::iterator iterator;
    typedef typename ht::const_iterator const_iterator;
};
//stl_hashtable.h
template<class Value,class Key,class HashFcn,
            class ExtractKey,class EqualKey,
            class Alloc>
class hashtable{
public:
    typedef Key key_type;
    typedef Value value_type;
    typedef HashFcn hasher;
    typedef EqualKey key_equal;
private:
    hasher hash;
    key_equal equals;
    ExtractKey get_key;
    typedef __hashtable_node<Value> node;
    vector<node*,Alloc> buckets;
    size_type num_elements;
public:
    typedef __hashtable_iterator<Value,Key,HashFcn,ExtractKey,EqualKey,
                Alloc> itertor;
    pair<iterator,bool> insert_unique(const value_type& obj);
    const_iterator find(const key_type& key) const;
};
template<class Value>
struct __hashtable_node{
    __hahstable_node* next;
    Value val;
};

●通过源码可以看到,结构上hash_map和hash_set跟map和set的完全类似,复用同一个hashtable实现key和key/value结构,hash_set传给hash_ table的是两个key,hash_map传给hash_table的是pair<const key,value>

●需要注意源码里面跟map/set源码类似,命名风格比较乱,这里比map和set还乱,hash_set模板参数居然用的Value命名,hash_map用的是Key和T命名。

2.模拟实现

2.1实现出复用哈希表的框架并支持insert

●unordered_map和unordered_set复用之前实现的哈希表。

●key参数用K,value参数用V,哈希表中数据类型,使用T。

●其次跟map和set相比而言unordered_map和unordered_set的模拟实现类结构更复杂一点,但是大框架和思路完全类似。一i那位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比较相等。

namespace Achieve{
    template<class K,class Hash=HashFunc<K>>
    class unordered_set{
        struct SetKeyOfT{
            const K& operator()(const K& kv){
                return key;
            }
        };
    public:
        bool insert(const K& key){
            return _ht.Insert(key);
        }
    private:
        hash_buckte::HashTable<K,K,SetKeyOfT,Hash> _ht;
    };
}
namespace Achieve{
    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:
        bool insert(const pair<K,V>& kv){
            return _ht.Insert(kv);
        }
    private:
        hash_buckte::HashTable<K,pair<K,V>,MapKeyOfT,Hash> _ht;
    };
}
inline unsigned long __stl_next_prime(unsigned long n){
    //Note:assumes long is at least 32 bits
    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, 3221225473, 4294967291
    };
    const unsigned long* first=__stl_prime_list;
    const unsigned long* last=__stl_prime_list+__stl_num_primes;
    const unsigned long* pos=lower_bound(first,last,n);
    return pos==last?*(last-1):*pos;
}
namespace hash_buckte{
    template<class T>
    struct HashNode{
        T _data;
        HashNode<T>* _next;
        HashNode(const T& data)
            :_data(data)
            ,_next(nullptr)
        {}
    };
    template<class K,class T,class KeyOfT,class Hash>
    class HashTable{
        typedef HashNode<T> Node;
    public:
        HashTable()
            :_tables(__stl_next_prime(0))
            ,_n(0)
        {}
        ~HashTable(){
            for(int i=0;i<_tables.size();i++){
                Node* cur=_tables[i];
                while(cur){
                    Node* next=cur->_next;
                    delete cur;
                    cur=next;
                }
                _tables[i]=nullptr;
            }
        }
        bool Insert(const T& data){
            KeyOfT kot;
            Hash hash;
            Iterator it=Find(kot(data));
            if(it!=End()) return false;
            //当负载因子为1时,扩容
            if(_n==_tables.size()){
                //直接复用Insert,不好
                /*HashTable<K,V> newht;
                newht._tables.resize(_tables.size()*2);
                for(int i=0;i<_tables.size();i++){
                    Node* cur=_tables[i];
                    while(cur){
                        newht.Insert(cur);
                        cur=cur->_next;
                    }
                }
                _tables.swap(newht);*/
                vector<Node*> newTable(__stl_next_prime(_tables.size()+1));
                //newht._tables.resize(_tables.size()*2);
                //newht._tables.resize(__stl_next_prime(_tables.size()+1));
                for(int i=0;i<_tables.size();i++){
                    Node* cur=_tables[i];
                    while(cur){
                        Node* next=cur->_next;
                        size_t hash1=hash(kot(cur->_data))%newTable.size();
                        //头插
                        cur->_next=newTable[hash1];
                        newTable[hash1]=cur;
                        cur=next;
                    }
                    _tables[i]=nullptr;
                }
                _tables.swap(newTable);
            }
            //头插
            size_t hash1=hash(kot(data))%_tables.size();
            Node* newNode=new Node(data);
            newNode->_next=_tables[hash1];
            _tables[hash1]=newNode;
            ++_n;
            return true;
        }
    private:
        vector<Node*> _tables;
        size_t _n;
    };
}

2.2支持iterator的实现

iterator核心源代码

template<class Value,class Key,class HashFcn,
            class ExtractKey,class EqualKey,class Alloc>
struct __hashtable_iterator{
    typedef hashtable<Value,Key,HashFcn,ExtractKey,EqualKey,Alloc>
            hashtable;
    typedef __hashtable_iterator<Value,Key,HashFcn,
                                ExtractKey,EqualKey,Alloc>
            iterator;
    typedef __hashtable_const_iterator<Value,Key,HashFcn,ExtraceKey,EqualKey,Alloc>
            const_iterator;
    typedef __hashtable_node<Value> node;
    typedef forward_iterator_tag iterator_vategory;
    typedef Value value_type;
    node* cur;
    hashtable* ht;
    __hashtable_iterator(node* n,hashtable* tab): cur(n),ht(tab) {}
    __hashtable_iterator() {}
    reference operator*() const { return cur->val;}
#ifndef __SGI_STL_NO_ARROW_OPERATOR
    pointer operator->() const {return &(operator*());}
#endif /*__SGI_STL_NO_ARROW_OPERATOR*/
    iterator& operator++();
    iterator operator++(int);
    bool operator==(const iterator& it) const {return cur==it.cur;}
    bool operator!=(const iterator& it) const {return cur!=it.cur;}
template<class V,class K,class HF,class ExK,class EqK,class A>
__hashtable_iterator<V,K,HF,ExK,EqK,A>&
__hashtable_iterator<V,K,HF,ExK,EqK,A>::operator++(){
    const node* old=cur;
    cur=cur->next;
    if(!cur){
        size_type bucket=ht->bkt_num(old->val);
        while(!curL&&++bucket<ht->buckets.size())
            cur=ht->buckets[bucket];
    }
    return *this;
}

iterator实现思路分析

●iterator实现的大框架跟list的iterator思路是一致的,用一个类型封装节点的指针,再通过重载运算符实现,迭代器像指针一样访问的行为,要注意哈希表的迭代器是单向迭代器。

●难点是operator++的实现。iterator中有一个指向节点的指针,若当前桶下面还有节点,则节点的指针指向下一个节点即可。若当前桶走完了,则需要想办法计算找到下一个桶。这里的难点反而是结构设计的问题,上面的源码,我们可以看到iterator中除了有节点的指针,还有哈希桶对象的指针,这样当前桶走完了,要计算下一个桶就相对容易多了,用key值计算出当前桶位置,依次往后找下一个不为空的桶即可。

●begin()返回第一个桶中的第一个节点指针构造的迭代器,这里end()返回迭代器可以用空表示。

●unoedered_set的iterator也不支持修改,把unordered_set的第二个模板参数改成const K即可,HashTable<k,const K,SetKeyOfT,Hash> _ht;

●unordered_map的iterator不支持修改key但是可以修改value,把unordered_map的第二个模板参数pair的第一个参数改成const K即可,HashTable<K,pair<const K,V>,MapKeyOfT,Hash> _ht;

2.3map支持[]

●unordered_map要支持[]主要需要修改insert返回值支持,修改HashTable中的insert返回值为pair<Iterator,bool> Insert(const T& data)

2.4Achieve::unordered_map和Achieve::unordered_set代码实现

namespace Achieve{
    template<class K,class Hash=HashFunc<K>>
    class unordered_set{
        struct KeyOfT{
            const K& operator()(const K& key){
                return key;
            }
        };
    public:
        typedef typename hash_buckte::HashTable<K,const K,KeyOfT,Hash>::Iterator iterator;
        typedef typename hash_buckte::HashTable<K,const K,KeyOfT,Hash>::ConstIterator const_iterator;
        iterator begin(){
            return _ht.Begin();
        }
        iterator end(){
            return _ht.End();
        }
        const_iterator begin() const{
            return _ht.Begin();
        }
        const_iterator end() const{
            return _ht.End();
        }
        pair<iterator,bool> insert(const K& key){
            return _ht.Insert(key);
        }
        iterator Find(const K& key){
            return _ht.Find(key);
        }
        bool erase(const K& key){
            return _ht.Erase(key);
        }
    private:
        hash_buckte::HashTable<K,const K,KeyOfT,Hash> _ht;
    };
            void print(const unordered_set<int>& s){
            unordered_set<int>::const_iterator it=s.begin();
            while(it!=s.end()){
                cout<<*it<<" ";
                ++it;
            }
            cout<<endl;
            for(auto e:s){
                cout<<e<<' ';
            }
            cout<<endl;
        }
        void test_set(){
            int a[]={3,11,86,88,1,881,5,6,7,6};
            unordered_set<int> s;
            for(auto e:a){
                s.insert(e);
            }
            unordered_set<int>::iterator it=s.begin();
            while(it!=s.end()){
                cout<<*it<<" ";
                ++it;
            }
            cout<<endl;
            for(auto e:s){
                cout<<e<<' ';
            }
            cout<<endl;
            print(s);
        }
}
namespace Achieve{
    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 hash_buckte::HashTable<K,pair<const K,V>,MapKeyOfT,Hash>::Iterator iterator;
        typedef typename hash_buckte::HashTable<K,pair<const K,V>,MapKeyOfT,Hash>::ConstIterator const_iterator;
        iterator begin(){
            return _ht.Begin();
        }
        iterator end(){
            return _ht.End();
        }
        const_iterator begin() const{
            return _ht.Begin();
        }
        const_iterator end() const{
            return _ht.End();
        }
        V& operator[](const K& key){
            pair<iterator,bool> ret=insert({key,V()});
            return ret.first->second;
        }
        pair<iterator,bool> insert(const pair<K,V>& kv){
            return _ht.Insert(kv);
        }
        iterator Find(const K& key){
            return _ht.Find(key);
        }
        bool erase(const K& key){
            return _ht.Erase(key);
        }
    private:
        hash_buckte::HashTable<K,pair<const K,V>,MapKeyOfT,Hash> _ht;
    };
    void test_map(){
        unordered_map<string,string> dict;
        dict.insert({"sort","排序"});
        dict.insert({"字符串","string"});
        dict.insert({"sort","排序"});
        dict.insert({"left","左边"});
        dict.insert({"right","右边"});
        dict["left"]="左边、剩余";
        dict["insert"]="插入";
        dict["string"];
        for(auto& kv:dict){
            cout<<kv.first<<":"<<kv.second<<endl;
        }
        cout<<endl;
        unordered_map<string,string>::iterator it=dict.begin();
        while(it!=dict.end()){
            it->second+='x';
            cout<<it->first<<":"<<it->second<<endl;
            ++it;
        }
        cout<<endl;
    }
}
template<class K>
struct HashFunc{
    size_t operator()(const K& key){
        return (size_t)key;
    }
};
//特化
template<>
struct HashFunc<string>{
    size_t operator()(const string& s){
        //BKDR
        size_t hash=0;
        for(auto ch:s){
            hash+=ch;
            hash*=131;
        }
        return hash;
    }
};
inline unsigned long __stl_next_prime(unsigned long n){
    //Note:assumes long is at least 32 bits
    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, 3221225473, 4294967291
    };
    const unsigned long* first=__stl_prime_list;
    const unsigned long* last=__stl_prime_list+__stl_num_primes;
    const unsigned long* pos=lower_bound(first,last,n);
    return pos==last?*(last-1):*pos;
}
namespace hash_buckte{
    template<class T>
    struct HashNode{
        T _data;
        HashNode<T>* _next;
        HashNode(const T& data)
            :_data(data)
            ,_next(nullptr)
        {}
    };
    //前置声明,防止HashIterator不认识HashTable
    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;
        const HT* _ht;//取模时要用
        HTIterator(Node* node,const HT* ht)
            :_node(node)
            ,_ht(ht)
        {}
        Ref operator*(){
            return _node->_data;
        }
        Ptr operator->(){
            return &_node->_data;
        }
        bool operator!=(const Self& s){
            return _node!=s._node;
        }
        Self& operator++(){
            if(_node->_next){
                _node=_node->_next;
            }
            else{
                //找下一个不为空的桶
                Hash hash;
                KeyOfT kot;
                size_t hash0=hash(kot(_node->_data))%_ht->_tables.size();
                ++hash0;
                while(hash0<_ht->_tables.size()){
                    _node=_ht->_tables[hash0];                    
                    if(_node){
                        break;
                    }
                    else ++hash0;
                }
                //所有桶都走完了,end()给的空的标识的_node
                if(hash0==_ht->_tables.size()){
                    _node=nullptr;
                }                
            }
            return *this;
        }
    };
    template<class K,class T,class KeyOfT,class Hash>
    class HashTable{
        //友元声明,为防止模板参数冲突,将参数改名
        template<class K1,class T1,class Ref,class Ptr,class KeyOfT1,class Hash1>
        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;
        HashTable()
            :_tables(__stl_next_prime(0))
            ,_n(0)
        {}
        ~HashTable(){
            for(int i=0;i<_tables.size();i++){
                Node* cur=_tables[i];
                while(cur){
                    Node* next=cur->_next;
                    delete cur;
                    cur=next;
                }
                _tables[i]=nullptr;
            }
        }
        Iterator Begin(){
            if(_n==0){
                return End();
            }
            for(int i=0;i<_tables.size();i++){
                Node* cur=_tables[i];
                if(cur) return Iterator(cur,this);;
            }
            return End();
        }
        Iterator End(){
            return Iterator(nullptr,this);
        }
        ConstIterator Begin() const{
            if(_n==0){
                return End();
            }
            for(int i=0;i<_tables.size();i++){
                Node* cur=_tables[i];
                if(cur) return ConstIterator(cur,this);;
            }
            return End();
        }
        ConstIterator End() const{
            return ConstIterator(nullptr,this);
        }
        pair<Iterator,bool> Insert(const T& data){
            KeyOfT kot;
            Hash hash;
            Iterator it=Find(kot(data));
            if(it!=End()) return {it,false};
            //当负载因子为1时,扩容
            if(_n==_tables.size()){
                //直接复用Insert,不好
                /*HashTable<K,V> newht;
                newht._tables.resize(_tables.size()*2);
                for(int i=0;i<_tables.size();i++){
                    Node* cur=_tables[i];
                    while(cur){
                        newht.Insert(cur);
                        cur=cur->_next;
                    }
                }
                _tables.swap(newht);*/
                vector<Node*> newTable(__stl_next_prime(_tables.size()+1));
                //newht._tables.resize(_tables.size()*2);
                //newht._tables.resize(__stl_next_prime(_tables.size()+1));
                for(int i=0;i<_tables.size();i++){
                    Node* cur=_tables[i];
                    while(cur){
                        Node* next=cur->_next;
                        size_t hash1=hash(kot(cur->_data))%newTable.size();
                        //头插
                        cur->_next=newTable[hash1];
                        newTable[hash1]=cur;
                        cur=next;
                    }
                    _tables[i]=nullptr;
                }
                _tables.swap(newTable);
            }
            //头插
            size_t hash1=hash(kot(data))%_tables.size();
            Node* newNode=new Node(data);
            newNode->_next=_tables[hash1];
            _tables[hash1]=newNode;
            ++_n;
            return {Iterator(newNode,this),true};
        }
        Iterator Find(const K& key){
            KeyOfT kot;
            Hash hash;
            size_t hash0=hash(key)%_tables.size();
            Node* cur=_tables[hash0];
            while(cur){
                if(kot(cur->_data)==key)
                    return Iterator(cur,this);
                cur=cur->_next;
            }
            return End();
        }
        bool Erase(const K& key){
            KeyOfT kot;
            Hash hash;
            //没有这个值
            if(!Find(key)) return false;
            size_t hash0=hash(key)%_tables.size();
            Node* cur=_tables[hash0];
            Node* prev=nullptr;
            while(cur){
                if(kot(cur->_data)==key){
                    //删除的节点是链表的头
                    if(!prev){
                        _tables[hash0]=cur->_next;
                    }
                    else{
                        prev->_next=cur->_next;
                    }
                    delete cur;
                    --_n;
                    return true;
                }
                else{
                    prev=cur;
                    cur=cur->_next;
                }
            }
            return false;
        }
    private:
        vector<Node*> _tables;
        size_t _n;
    };
}

以上就是C++中用哈希表封装myunordered_set和myunordered_map方法详解的详细内容,更多关于C++封装myunordered_set和myunordered_map的资料请关注脚本之家其它相关文章!

相关文章

  • C语言实现绘制贝塞尔曲线的函数

    C语言实现绘制贝塞尔曲线的函数

    贝塞尔曲线,又称贝兹曲线或贝济埃曲线,是应用于二维图形应用程序的数学曲线。本文将利用C语言实现绘制贝塞尔曲线的函数,需要的可以参考一下
    2022-12-12
  • C++ override关键字使用详解

    C++ override关键字使用详解

    这篇文章主要介绍了C++ override关键字使用详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-01-01
  • C++中new与delete、malloc与free应用分析

    C++中new与delete、malloc与free应用分析

    这篇文章主要介绍了C++中new与delete、malloc与free应用分析,很重要的概念,需要的朋友可以参考下
    2014-08-08
  • C语言中栈的两种实现方法详解

    C语言中栈的两种实现方法详解

    栈只允许在一端进行插入或删除操作的线性表。首先栈是一种线性表,但是限定这种线性表只能在某一端进行插入和删除操作,这篇文章主要介绍了C语言对栈的实现基本操作
    2021-08-08
  • c程序生成并使用共享库的操作方法

    c程序生成并使用共享库的操作方法

    在C语言开发中,共享库可以减少程序体量并实现功能共享,本文详细介绍了如何创建一个实现基本数学功能的共享库,并展示了其他程序如何利用这个库,步骤包括编写源代码、编译成目标文件、链接成共享库以及如何在其他程序中使用这个库
    2024-09-09
  • C++引用、内联函数与nullptr超全解析(新手避坑指南)

    C++引用、内联函数与nullptr超全解析(新手避坑指南)

    这篇文章主要介绍了C++引用、内联函数与nullptr的相关资料,引用提供别名,简化代码和提高性能,内联函数通过减少栈帧开销优化性能,但不能滥用,nullptr替代NULL,提供类型安全的空指针表示,需要的朋友可以参考下
    2025-12-12
  • C语言内存的动态分配比较malloc和realloc的区别

    C语言内存的动态分配比较malloc和realloc的区别

    这篇文章主要介绍了C语言内存的动态分配比较malloc和realloc的区别,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是本文的详细内容,需要的朋友可以参考下
    2021-07-07
  • C++实现经典24点纸牌益智游戏

    C++实现经典24点纸牌益智游戏

    这篇文章主要介绍了C++实现经典24点纸牌益智游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-03-03
  • 浅谈c语言中类型隐性转换的坑

    浅谈c语言中类型隐性转换的坑

    下面小编就为大家带来一篇浅谈c语言中类型隐性转换的坑。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-08-08
  • 基于C语言实现个人通讯录管理系统

    基于C语言实现个人通讯录管理系统

    这篇文章主要为大家详细介绍了基于C语言实现个人通讯录管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-06-06

最新评论