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/stl_hash_map/stl_hash_set/stl_hashtable.h中
hash_map和hash_set的实现结构框架核⼼部分截取出来如下:
stl_hash_set:
cpp
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:
cpp
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,
select1st<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> iterator;
pair<iterator, bool> insert_unique(const value_type& obj);
const_iterator find(const key_type& key) const;
};
template <class Value>
struct __hashtable_node
{
__hashtable_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. 模拟实现unordered_map和unordered_set
2.1 实现出复用哈希表的框架,并支持insert
- 参考源码框架,unordered_map和unordered_set复⽤之前我们实现的哈希表。
- 我们这里相比源码调整⼀下,key参数就用K,value参数就⽤V,哈希表中的数据类型,我们使用T。
- 其次跟map和set相比而言unordered_map和unordered_set的模拟实现类结构更复杂⼀点,但是大框架和思路是完全类似的。因为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比较相等,具体细节参考如下代码实现。
实现步骤:
- 实现哈希表
- 封装unordered_map和unordered_set的框架 解决KeyOfT
- iterator
- const_iterator
- key不支持修改的问题
- perator[]
UnorderedSet.h:
cpp
#pragma once
#include"HashTable.h"
namespace xiaohan
{
template<class K, class Hash = HashFunc<K>>
class unordered_map
{
// 仿函数,set(K)和map(pair)比较大小时不一样
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
public:
bool insert(const K& key)
{
return _ht.Insert(key);
}
private:
hash_bucket::HashTable<K, K, SetKeyOfT, Hash> _ht;
};
}
UnorderedMap.h:
cpp
#pragma once
#include"HashTable.h"
namespace xiaohan
{
template<class K, class Hash = HashFunc<K>>
class unordered_map
{
// 仿函数,set(K)和map(pair)比较大小时不一样
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
public:
bool insert(const K& key)
{
return _ht.Insert(key);
}
private:
hash_bucket::HashTable<K, K, SetKeyOfT, Hash> _ht;
};
}
HashTable.h:
与上一章介绍的HashTable.h区别:用T替代了K,V,解决了KeyOfT问题
cpp
#pragma once
#pragma once
#include<vector>
#include<string>
using namespace std;
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
};
inline unsigned long __stl_next_prime(unsigned long n)
{
const unsigned long* first = __stl_prime_list;
const unsigned long* last = __stl_prime_list + __stl_num_primes;
// >= n
const unsigned long* pos = lower_bound(first, last, n);
return pos == last ? *(last - 1) : *pos;
}
template<class K>
struct HashFunc
{
size_t operator()(const K& key)
{
return (size_t)key;
}
};
//特化
template<>
struct HashFunc<string>
{
/* 字符串转换成整形,可以把字符ascii码相加即可
但是直接相加的话,类似"abcd"和"bcad"这样的字符串计算出是相同的
这⾥我们使⽤BKDR哈希的思路,用上次的计算结果去乘以⼀个质数,这个质数⼀般取31,131
等效果会比较好*/
size_t operator()(const string& key)
{
// abcd
size_t hash = 0;
for (auto e : key)
{
hash = hash + e;
hash = hash * 131;
}
return hash;
}
};
namespace hash_bucket
{
template<class T>
struct HashNode
{
T _data;
HashNode<T>* _next;
HashNode(const T data)
:_data(data)
, _next(nullptr)
{}
};
// hash_bucket::HashTable<K, pair<K, V>, MapKeyOfT> _ht;
// hash_bucket::HashTable<K, K, SetKeyOfT> _ht;
template<class K, class T, class KeyOfT, class Hash>
class HashTable
{
typedef HashNode<T> Node;
public:
HashTable()
:_tables(__stl_next_prime(1), 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 = 0;
}
bool Insert(const T& data)
{
KeyOfT kot;
if (Find(kot(data)))
return false;
Hash hs;
// 负载因子 == 1就开始扩容
if (_n == _tables.size())
{
// 方法二, 只创建vector表,直接将旧表的结点链接到新表中
vector<Node*> newtables(__stl_next_prime(_tables.size() + 1), nullptr);
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(kot(data)) % _tables.size();
// 头插法
Node* newnode = new Node(kv);
newnode->_next = _tables[hashi];
_tables[hashi] = newnode;
++_n;
return true;
}
Node* 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;
}
cur = cur->_next;
}
return nullptr;
}
bool Erase(const K& key)
{
KeyOfT kot;
Hash hs;
size_t hashi = hs(key) % _tables.size();
Node* pre = nullptr;
Node* cur = _tables[hashi];
while (cur)
{
// 删除
if (kot(cur->_data) == key)
{
// 桶中第一个结点
if (pre == nullptr)
{
_tables[hashi] = cur->_next;
}
else
{
pre->_next = cur->_next;
}
--_n;
delete cur;
return true;
}
pre = cur;
cur = cur->_next;
}
return false;
}
private:
vector<Node*> _tables; // 指针数组
size_t _n;
};
}
2.2 支持iterator的实现
iterator实现思路分析:

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

由于HTIterator的类定义在HashTable之前,而且两者类内都有对方,所以HTIterator前面要添加前置声明

cpp
// 前置声明
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* _pht; // 哈希表的指针
HTIterator(Node* node, const HT* pht)
:_node(node)
, _pht(pht)
{}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
Self& operator++()
{
KeyOfT kot;
Hash hs;
if (_node->_next) // 当前桶没走完
{
_node = _node->_next;
}
else // 当前桶走完了,找到下一个桶的第一个节点
{
// 算出当前桶的位置
size_t hashi = hs(kot(_node->_data)) % _pht->_tables.size();
++hashi;
while(hashi < _pht->_tables.size())
{
if (_pht->_tables[hashi]) // 找到下一个不为空的桶
{
_node = _pht->_tables[hashi];
break;
}
else
{
++hashi;
}
}
if (hashi == _pht->_tables.size()) // 最后一个桶走完了,要++到end()位置
{
// end()中_node是空
return nullptr;
}
}
return *this;
}
bool operator!=(const Self& s) const
{
return _node != s._node;
}
bool operator==(const Self& s) const
{
return _node == s._node;
}
};
注意:HashTable中的HTIterator也要有友元声明

cpp
class HashTable
{
// 友元声明
template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
friend struct HIIterator;
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()
{
if (_n == 0)
{
return End();
}
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
{
if (_n == 0)
{
return End();
}
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(1), nullptr)
, _n(0)
{}
...
unordered_set的迭代器也不支持修改,把unordered_set的第二个模板参数改成const K即可:

unordered_map的iterator不支持修改key但是可以修改value,我们把unordered_map的第二个
模板参数pair的第一个参数改成const K即可

2.3 map支持[]

2.4 unordered_map和unordered_set代码实现
UnorderedSet.h:
cpp
#pragma once
#include"HashTable.h"
namespace xiaohan
{
template<class K, class Hash = HashFunc<K>>
class unordered_set
{
// 仿函数,set(K)和map(pair)比较大小时不一样
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
public:
typedef typename hash_bucket::HashTable<K, const K, SetKeyOfT, Hash>::Iterator iterator;
typedef typename hash_bucket::HashTable<K, const K, SetKeyOfT, 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_bucket::HashTable<K, const K, SetKeyOfT, Hash> _ht;
};
}
UnorderedMap.h:
cpp
#pragma once
#include"HashTable.h"
namespace xiaohan
{
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_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash>::Iterator iterator;
typedef typename hash_bucket::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();
}
pair<iterator, bool> insert(const pair<K, V>& kv)
{
return _ht.Insert(kv);
}
V& operator[](const K& key)
{
pair<iterator, bool> ret = insert({ key, V() });
return ret.first->second;
}
iterator find(const K& key)
{
return _ht.Find(key);
}
bool erase(const K& key)
{
return _ht.Erase(key);
}
private:
hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash> _ht;
};
}
HashTable.h:
cpp
#pragma once
#pragma once
#include<vector>
#include<string>
using namespace std;
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
};
inline unsigned long __stl_next_prime(unsigned long n)
{
const unsigned long* first = __stl_prime_list;
const unsigned long* last = __stl_prime_list + __stl_num_primes;
// >= n
const unsigned long* pos = lower_bound(first, last, n);
return pos == last ? *(last - 1) : *pos;
}
template<class K>
struct HashFunc
{
size_t operator()(const K& key)
{
return (size_t)key;
}
};
//特化
template<>
struct HashFunc<string>
{
/* 字符串转换成整形,可以把字符ascii码相加即可
但是直接相加的话,类似"abcd"和"bcad"这样的字符串计算出是相同的
这⾥我们使⽤BKDR哈希的思路,用上次的计算结果去乘以⼀个质数,这个质数⼀般取31,131
等效果会比较好*/
size_t operator()(const string& key)
{
// abcd
size_t hash = 0;
for (auto e : key)
{
hash = hash + e;
hash = hash * 131;
}
return hash;
}
};
namespace hash_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 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* _pht; // 哈希表的指针
HTIterator(Node* node, const HT* pht)
:_node(node)
, _pht(pht)
{}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
Self& operator++()
{
KeyOfT kot;
Hash hs;
if (_node->_next) // 当前桶没走完
{
_node = _node->_next;
}
else // 当前桶走完了,找到下一个桶的第一个节点
{
// 算出当前桶的位置
size_t hashi = hs(kot(_node->_data)) % _pht->_tables.size();
++hashi;
while(hashi < _pht->_tables.size())
{
if (_pht->_tables[hashi]) // 找到下一个不为空的桶
{
_node = _pht->_tables[hashi];
break;
}
else
{
++hashi;
}
}
if (hashi == _pht->_tables.size()) // 最后一个桶走完了,要++到end()位置
{
// end()中_node是空
_node = nullptr;
}
}
return *this;
}
bool operator!=(const Self& s) const
{
return _node != s._node;
}
bool operator==(const Self& s) const
{
return _node == s._node;
}
};
// hash_bucket::HashTable<K, pair<K, V>, MapKeyOfT> _ht;
// hash_bucket::HashTable<K, K, SetKeyOfT> _ht;
template<class K, class T, class KeyOfT, class Hash>
class HashTable
{
// 友元声明
template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
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()
{
if (_n == 0)
{
return End();
}
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
{
if (_n == 0)
{
return End();
}
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(1), 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 = 0;
}
pair<Iterator, bool> Insert(const T& data)
{
KeyOfT kot;
auto it = Find(kot(data));
if (it != End())
return { it, false };
Hash hs;
// 负载因子 == 1就开始扩容
if (_n == _tables.size())
{
// 方法二, 只创建vector表,直接将旧表的结点链接到新表中
vector<Node*> newtables(__stl_next_prime(_tables.size() + 1), nullptr);
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(kot(data)) % _tables.size();
// 头插法
Node* newnode = new Node(data);
newnode->_next = _tables[hashi];
_tables[hashi] = newnode;
++_n;
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 {nullptr, this};
}
bool Erase(const K& key)
{
KeyOfT kot;
Hash hs;
size_t hashi = hs(key) % _tables.size();
Node* pre = nullptr;
Node* cur = _tables[hashi];
while (cur)
{
// 删除
if (kot(cur->_data) == key)
{
// 桶中第一个结点
if (pre == nullptr)
{
_tables[hashi] = cur->_next;
}
else
{
pre->_next = cur->_next;
}
--_n;
delete cur;
return true;
}
pre = cur;
cur = cur->_next;
}
return false;
}
private:
vector<Node*> _tables; // 指针数组
size_t _n;
// std::vector<std::list<K, V>> _tables;
};
}
测试代码:
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<unordered_map>
using namespace std;
#include"UnorderedSet.h"
#include"UnorderedMap.h"
void Print(const xiaohan::unordered_set<int>& s)
{
xiaohan::unordered_set<int>::const_iterator it = s.begin();
while (it != s.end())
{
// *it = 1;
cout << *it << " ";
++it;
}
cout << endl;
}
int main()
{
xiaohan::unordered_set<int> us;
us.insert(3);
us.insert(1000);
us.insert(2);
us.insert(102);
us.insert(2111);
us.insert(22);
xiaohan::unordered_set<int>::iterator it = us.begin();
while (it != us.end())
{
//*it = 1;
cout << *it << " ";
++it;
}
cout << endl;
Print(us);
xiaohan::unordered_map<string, string> dict;
dict.insert({ "string", "ַ字符串" });
dict.insert({ "string", "ַ字符串1" });
dict.insert({ "left", "左边" });
dict.insert({ "right", "右边" });
// 可修改
dict["left"] = "左边,剩余";
//
dict["insert"];
//
dict["map"] = "地图";
for (auto& [k, v] : dict)
{
//k += 'x';
//v += 'x';
cout << k << ":" << v << endl;
}
return 0;
}
结果:
