前言:
前面我们学习了unordered_map和unordered_set容器,比较了他们和map、set的查找效率,我们发现他们的效率比map、set高,进而我们研究他们的底层是由哈希实现。哈希是一种直接映射的方式,所以查找的效率很快。与学习红黑树和map、set的思路一样,我们现在学完了unordered_map和unordered_set,本章将模拟实现底层结构来封装该容器!
作者建议在阅读本章前,可以先去看一下前面的红黑树封装map和set——红黑树封装map和set
这两篇文章都重在强调泛型编程的思想,上一篇由于是初认识,作者讲解的会更详细一点~
目录
2、operator*和operator->及operator!=和operator==的模拟实现
(四)封装unordered_map和unordered_set
我们学习过知道,unordered_map和unordered_set容器存放的结点并不一样,为了让它得到复用,我们就需要对哈希桶进行改造,将哈希桶改造的更加泛型一点,既符合Key模型,也符合Key_Value模型。

所以我们这里还是和封装map和set时一样,无论是Key还是Key_Value,都用一个类型T来接收,这里高维度的泛型哈希表中,实现还是用的是Kye_Value模型,K是不能省略的,同样的查找和删除要用,故我们可以引出两个容器各自模板参数类型。

如何取到想要的数据:
同时我们再给每个容器配一个哈希函数。
通过上面1和2,我们可以把各自存放的数据泛化成data:

这样我们哈希桶的模板参数算是完成了
我们把上一篇文章封装的哈希桶拿来改造:
- //K --> 键值Key,T --> 数据
- //unordered_map ->HashTable<K, pair<K, V>, MapKeyOfT> _ht;
- //unordered_set ->HashTable<K, K, SetKeyOfT> _ht;
- template<class K, class T, class KeyOfT, class HashFunc>
- class HashTable
- {
- template<class K, class T, class KeyOfT, class HashFunc>
- friend class __HTIterator;
-
- typedef HashNode<T> Node;
- public:
- typedef __HTIterator<K, T, KeyOfT, HashFunc> iterator;
-
- iterator begin()
- {
- for (size_t i = 0; i < _tables.size(); i++)
- {
- Node* cur = _tables[i];
- if (cur)
- {
- return iterator(cur, this);
- }
- }
-
- return end();
- }
-
- iterator end()
- {
- return iterator(nullptr, this);
- }
-
- ~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;
- }
- }
-
- size_t GetNextPrime(size_t prime)
- {
- const int PRIMECOUNT = 28;
- static const size_t primeList[PRIMECOUNT] =
- {
- 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
- };
-
- //获取比prime大那一个素数
- size_t i = 0;
- for (i = 0; i < PRIMECOUNT; i++)
- {
- if (primeList[i] > prime)
- return primeList[i];
- }
-
- return primeList[i];
- }
-
- pair<iterator, bool> Insert(const T& data)
- {
- HashFunc hf;
- KeyOfT kot;
-
- iterator pos = Find(kot(data));
- if (pos != end())
- {
- return make_pair(pos, false);
- }
-
- //负载因子 == 1 扩容 -- 平均每个桶挂一个结点
- if (_tables.size() == _n)
- {
- //size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
- size_t newSize = GetNextPrime(_tables.size());
-
- if (newSize != _tables.size())
- {
- vector<Node*> newTable;
- newTable.resize(newSize, nullptr);
-
- //遍历旧表
- for (size_t i = 0; i < _tables.size(); i++)
- {
- Node* cur = _tables[i];
-
- //再对每个桶挨个遍历
- while (cur)
- {
- Node* next = cur->_next;
- size_t hashi = hf(kot(cur->_data)) % newSize;
-
- //转移到新的表中
- cur->_next = newTable[hashi];
- newTable[hashi] = cur;
- cur = next;
- }
-
- //将原表置空
- _tables[i] = nullptr;
- }
- newTable.swap(_tables);
- }
-
- }
-
- size_t hashi = hf(kot(data));
- hashi %= _tables.size();
-
- //头插到对应的桶即可
- Node* newnode = new Node(data);
- newnode->_next = _tables[hashi];
- _tables[hashi] = newnode;
-
- //有效数据加一
- _n++;
-
- return make_pair(iterator(newnode, this), true);
- }
-
- iterator Find(const K& key)
- {
- if (_tables.size() == 0)
- {
- return iterator(nullptr, this);
- }
-
- KeyOfT kot;
- HashFunc hf;
- size_t hashi = hf(key);
- //size_t hashi = HashFunc()(key);
-
- hashi %= _tables.size();
- Node* cur = _tables[hashi];
-
- //找到指定的桶之后,顺着单链表挨个找
- while (cur)
- {
- if (kot(cur->_data) == key)
- {
- return iterator(cur, this);
- }
-
- cur = cur->_next;
- }
-
- //没找到返回空
- return iterator(nullptr, this);
- }
-
- bool Erase(const K& key)
- {
- if (_tables.size() == 0)
- {
- return false;
- }
-
- HashFunc hf;
- KeyOfT kot;
- size_t hashi = hf(key);
- hashi %= _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;
-
- return true;
- }
-
- prev = cur;
- cur = cur->_next;
- }
-
- return false;
- }
- private:
- //指针数组
- vector<Node*> _tables;
- size_t _n = 0;
- };
主要改造的地方就是上述所注意的地方:
还有对扩容的二次思考:
研究表明:除留余数法,最好模一个素数



这两组和之前实现的一模一样,大家自行理解。

注:

思路:
注意:
unordered_map和unordered_set是不支持反向迭代器的,从底层结构我们也能很好的理解(单链表找不了前驱)所以不支持实现迭代器的operator- -
最后注意一点,我们需要知道哈希桶大小,所以不仅要传结点地址,还要传一个哈希桶,这样才能知道其大小,除此,由于哈希桶改造在后面,所以我们要在前面声明一下:

- #include<vector>
- #include<string>
- #include<iostream>
- using namespace std;
-
- template<class K>
- struct DefaultHash
- {
- size_t operator()(const K& key)
- {
- return (size_t)key;
- }
- };
-
- template<>
- struct DefaultHash<string>
- {
- size_t operator()(const string& key)
- {
- //BKDR
- size_t hash = 0;
- for (auto ch : key)
- {
- hash = hash * 131 + ch;
- }
-
- return hash;
- }
- };
-
- namespace Bucket
- {
- 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 HashFunc>
- class HashTable;
-
- //哈希桶的迭代器
- template<class K, class T, class KeyOfT, class HashFunc>
- class __HTIterator
- {
- typedef HashNode<T> Node;
- typedef __HTIterator<K, T, KeyOfT, HashFunc> Self;
- public:
- Node* _node;
-
- __HTIterator() {};
-
- //编译器的原则是向上查找(定义必须在前面,否则必须先声明)
- HashTable<K, T, KeyOfT, HashFunc>* _pht;
-
- __HTIterator(Node* node, HashTable<K, T, KeyOfT, HashFunc>* pht)
- :_node(node)
- , _pht(pht)
- {}
-
- Self& operator++()
- {
- if (_node->_next)
- {
- _node = _node->_next;
- }
- else//当前桶已经走完了,要走下一个桶
- {
- KeyOfT kot;
- HashFunc hf;
- size_t hashi = hf(kot(_node->_data)) % _pht->_tables.size();
- hashi++;
-
- //找下一个不为空的桶 -- 访问到了哈希表中私有的成员(友元)
- for (; hashi < _pht->_tables.size(); hashi++)
- {
- if (_pht->_tables[hashi])
- {
- _node = _pht->_tables[hashi];
- break;
- }
- }
-
- //没有找到不为空的桶,用nullptr去做end标识
- if (hashi == _pht->_tables.size())
- {
- _node = nullptr;
- }
- }
-
- return *this;
- }
-
- T& operator*()
- {
- return _node->_data;
- }
-
- T* operator->()
- {
- return &_node->_data;
- }
-
- bool operator!=(const Self& s) const
- {
- return _node != s._node;
- }
-
- bool operator==(const Self& s) const
- {
- return _node == s._node;
- }
- };
-
- //K --> 键值Key,T --> 数据
- //unordered_map ->HashTable<K, pair<K, V>, MapKeyOfT> _ht;
- //unordered_set ->HashTable<K, K, SetKeyOfT> _ht;
- template<class K, class T, class KeyOfT, class HashFunc>
- class HashTable
- {
- template<class K, class T, class KeyOfT, class HashFunc>
- friend class __HTIterator;
-
- typedef HashNode<T> Node;
- public:
- typedef __HTIterator<K, T, KeyOfT, HashFunc> iterator;
-
- iterator begin()
- {
- for (size_t i = 0; i < _tables.size(); i++)
- {
- Node* cur = _tables[i];
- if (cur)
- {
- return iterator(cur, this);
- }
- }
-
- return end();
- }
-
- iterator end()
- {
- return iterator(nullptr, this);
- }
-
- ~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;
- }
- }
-
- size_t GetNextPrime(size_t prime)
- {
- const int PRIMECOUNT = 28;
- static const size_t primeList[PRIMECOUNT] =
- {
- 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
- };
-
- //获取比prime大那一个素数
- size_t i = 0;
- for (i = 0; i < PRIMECOUNT; i++)
- {
- if (primeList[i] > prime)
- return primeList[i];
- }
-
- return primeList[i];
- }
-
- pair<iterator, bool> Insert(const T& data)
- {
- HashFunc hf;
- KeyOfT kot;
-
- iterator pos = Find(kot(data));
- if (pos != end())
- {
- return make_pair(pos, false);
- }
-
- //负载因子 == 1 扩容 -- 平均每个桶挂一个结点
- if (_tables.size() == _n)
- {
- //size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
- size_t newSize = GetNextPrime(_tables.size());
-
- if (newSize != _tables.size())
- {
- vector<Node*> newTable;
- newTable.resize(newSize, nullptr);
-
- //遍历旧表
- for (size_t i = 0; i < _tables.size(); i++)
- {
- Node* cur = _tables[i];
-
- //再对每个桶挨个遍历
- while (cur)
- {
- Node* next = cur->_next;
- size_t hashi = hf(kot(cur->_data)) % newSize;
-
- //转移到新的表中
- cur->_next = newTable[hashi];
- newTable[hashi] = cur;
- cur = next;
- }
-
- //将原表置空
- _tables[i] = nullptr;
- }
- newTable.swap(_tables);
- }
-
- }
-
- size_t hashi = hf(kot(data));
- hashi %= _tables.size();
-
- //头插到对应的桶即可
- Node* newnode = new Node(data);
- newnode->_next = _tables[hashi];
- _tables[hashi] = newnode;
-
- //有效数据加一
- _n++;
-
- return make_pair(iterator(newnode, this), true);
- }
-
- iterator Find(const K& key)
- {
- if (_tables.size() == 0)
- {
- return iterator(nullptr, this);
- }
-
- KeyOfT kot;
- HashFunc hf;
- size_t hashi = hf(key);
- //size_t hashi = HashFunc()(key);
-
- hashi %= _tables.size();
- Node* cur = _tables[hashi];
-
- //找到指定的桶之后,顺着单链表挨个找
- while (cur)
- {
- if (kot(cur->_data) == key)
- {
- return iterator(cur, this);
- }
-
- cur = cur->_next;
- }
-
- //没找到返回空
- return iterator(nullptr, this);
- }
-
- bool Erase(const K& key)
- {
- if (_tables.size() == 0)
- {
- return false;
- }
-
- HashFunc hf;
- KeyOfT kot;
- size_t hashi = hf(key);
- hashi %= _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;
-
- return true;
- }
-
- prev = cur;
- cur = cur->_next;
- }
-
- return false;
- }
- private:
- //指针数组
- vector<Node*> _tables;
- size_t _n = 0;
- };
- }
有了上面的哈希桶的改装,我们这里的对map和set的封装就显得很得心应手了。
unordered_map的封装:
- #include "HashTable.h"
-
- namespace zc
- {
- template<class K, class V, class HashFunc = DefaultHash<K>>
- class unordered_map
- {
- struct MapKeyOfT
- {
- const K& operator()(const pair<K, V>& kv)
- {
- return kv.first;
- }
- };
- public:
- typedef typename Bucket::HashTable<K, pair<K, V>, MapKeyOfT, HashFunc>::iterator iterator;
-
- iterator begin()
- {
- return _ht.begin();
- }
-
- iterator end()
- {
- return _ht.end();
- }
-
- 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);
- }
-
- V& operator[](const K& key)
- {
- pair<iterator, bool> ret = insert(make_pair(key, V()));
- return ret.first->second;
- }
-
- private:
- Bucket::HashTable<K, pair<K, V>, MapKeyOfT, HashFunc> _ht;
- };
-
- void test_map()
- {
- unordered_map<string, string> dict;
- dict.insert(make_pair("sort", "排序"));
- dict.insert(make_pair("left", "左边"));
- dict.insert(make_pair("left", "下面"));
- dict["string"];
- dict["left"] = "上面";
- dict["string"] = "字符串";
-
- unordered_map<string, string>::iterator it = dict.begin();
- while (it != dict.end())
- {
- cout << it->first << " " << it->second << endl;
- ++it;
- }
- cout << endl;
-
- for (auto e : dict)
- {
- cout << e.first << " " << e.second << endl;
- }
- }
-
- }
这里unordered_map中的operator[ ]我们知道其原理之后,模拟实现就非常方便,直接调用插入函数,控制好参数和返回值即可。
对unordered_set的封装:
- #include "HashTable.h"
-
- #include "HashTable.h"
-
- namespace zc
- {
- template<class K, class HashFunc = DefaultHash<K>>
- class unordered_set
- {
-
- struct SetKeyOfT
- {
- const K& operator()(const K& key)
- {
- return key;
- }
- };
-
- public:
- //2.48
- typedef typename Bucket::HashTable<K, K, SetKeyOfT, HashFunc>::iterator iterator;
-
- iterator begin()
- {
- return _ht.begin();
- }
-
- iterator end()
- {
- 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:
- Bucket::HashTable<K, K, SetKeyOfT, HashFunc> _ht;
- };
-
- struct Date
- {
- Date(int year = 1, int month = 1, int day = 1)
- :_year(year)
- , _month(month)
- , _day(day)
- {}
-
- bool operator==(const Date& d) const
- {
- return _year == d._year
- && _month == d._month
- && _day == d._day;
- }
-
- int _year;
- int _month;
- int _day;
- };
-
- struct DateHash
- {
- size_t operator()(const Date& d)
- {
- //return d._year + d._month + d._day;
- size_t hash = 0;
- hash += d._year;
- hash *= 131;
- hash += d._month;
- hash *= 1313;
- hash += d._day;
-
- //cout << hash << endl;
-
- return hash;
- }
- };
-
- void test_set()
- {
- unordered_set<int> s;
- //set<int> s;
- s.insert(2);
- s.insert(3);
- s.insert(1);
- s.insert(2);
- s.insert(5);
- s.insert(12);
-
- unordered_set<int>::iterator it = s.begin();
- //auto it = s.begin();
- while (it != s.end())
- {
- cout << *it << " ";
- ++it;
- }
- cout << endl;
-
- for (auto e : s)
- {
- cout << e << " ";
- }
- cout << endl;
-
- unordered_set<Date, DateHash> sd;
- sd.insert(Date(2022, 3, 4));
- sd.insert(Date(2022, 4, 3));
- }
- }
最后大家可以利用代码中给的测试函数进行测试!
感谢你的阅读!