
🎬 博主名称 :键盘敲碎了雾霭
🔥 个人专栏 : 《C语言》《数据结构》 《C++》 《Matlab》 《Python》
⛺️指尖敲代码,雾霭皆可破

文章目录
- 一、源码分析
-
- [1.1 stl_hash_set](#1.1 stl_hash_set)
- [1.2 stl_hash_map](#1.2 stl_hash_map)
- [1.3 stl_hashtable.h](#1.3 stl_hashtable.h)
- [1.4 总体思路](#1.4 总体思路)
- 二、模拟实现
-
- [2.1 实现哈希表](#2.1 实现哈希表)
- [2.2 iterator的实现](#2.2 iterator的实现)
- [2.3 map里\[\]的实现](#2.3 map里[]的实现)
- [2.4 易错点](#2.4 易错点)
-
- [2.4.1 **前置声明**](#2.4.1 前置声明)
- [2.4.2 友元声明](#2.4.2 友元声明)
- [2.4.3 const范围](#2.4.3 const范围)
- 三、完整代码
-
- [3.1 Unorderedset.h](#3.1 Unorderedset.h)
- [3.2 Unorderedmap.h](#3.2 Unorderedmap.h)
- [3.3 HashTable.h](#3.3 HashTable.h)
- 文章结语
一、源码分析
SGI-STL30实现了哈希表,容器的名字是hash_map和hash_set,他是作为非标准的容器出现的,非标准是指非C++标准规定必须实现的,源代码在hash_map/hash_set/stl_hash_set/stl_hashtable.h中
1.1 stl_hash_set
cpp
// 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(); }
};
1.2 stl_hash_map
cpp
// 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,
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;
};
1.3 stl_hashtable.h
cpp
// 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;
};
1.4 总体思路
通过源码可以看到,结构上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类似
不懂的小伙伴看这篇【C++进阶】红黑树模拟实现mymap和myset
二、模拟实现
2.1 实现哈希表
参考源码框架,unordered_map和unordered_set复用之前我们实现的哈希表。
之前已经实现过了,不会的可以看这篇
【C++进阶】哈希表实现(哈希函数 + 冲突处理 + 完整代码)
因为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比较相等
2.2 iterator的实现
- iterator实现的大框架跟list的iterator思路是一致的,用一个类型封装结点的指针,再通过重载运算符实现,迭代器像指针一样访问的行为,要注意的是哈希表的迭代器是单向迭代器
- 这里的难点是operator++的实现。iterator中有一个指向结点的指针,如果当前桶下面还有结点,则结点的指针指向下一个结点即可。如果当前桶走完了,则需要想办法计算找到下一个桶。这里的难点是反而是结构设计的问题,需要节点指针和表指针两个变量,这样当前桶走完了,要计算下一个桶就相对容易多了,用key值计算出当前桶位置,依次往后找下一个不为空的桶即可。

- begin()返回第一个桶中第一个节点指针构造的迭代器,这里end()返回迭代器可以用空表示。
- unordered_set的iterator也不支持修改,我们把unordered_set的第二个模板参数改成const K即可
cpp
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> table;
typedef HTIterator<K, T, Ref, Ptr, KeyofT, Hash> Self;
Node* _node;
const table* _tb;
HTIterator(Node* node, const table* tb)//要const修饰
:_node(node)
, _tb(tb)
{
}
T& operator*()
{
return _node->_data;
}
T* operator->()
{
return &_node->_data;
}
bool operator==(const Self& tmp)
{
return _node == tmp._node;
}
bool operator!=(const Self& tmp)
{
return _node != tmp._node;
}
Self& operator++()
{
KeyofT kot;
Hash hst;
if (_node->_next)
{
_node = _node->_next;
}
else
{
size_t hash = hst(kot(_node->_data)) % _tb->_table.size();//私有成员
++hash;
while (hash < _tb->_table.size())
{
_node = _tb->_table[hash];
if (_node)
{
break;
}
else
{
hash++;
}
}
if (hash == _tb->_table.size())
{
_node = nullptr;
}
}
return *this;
}
};
2.3 map里\[\]的实现
核心是修改修改HashTable中的insert返回值为pair<ITERATOR,bool> ,这样既能支持修改和插入
cpp
V& operator[](const K& Key)
{
pair<iterator, bool> ret = _table.Insert({ Key,V() });
return ret.first->second;
}
2.4 易错点
2.4.1 前置声明
由于类HashTable和类HTIterator相互依赖
需要加类模板的前置声明
cpp
template<class K, class T, class KeyofT, class Hash >
class HashTable;
2.4.2 友元声明
在迭代器里实现++时,出现了使用类HashTable的私有成员需加上友元声明

cpp
template<class K, class T, class Ref, class Ptr, class KeyofT, class Hash>
friend struct HTIterator;
2.4.3 const范围
在实现const迭代器,传递的是const修饰的表指针,需要更改

cpp
HTIterator(Node* node, const table* tb)//要const修饰
:_node(node)
, _tb(tb)
{
}
三、完整代码
3.1 Unorderedset.h
cpp
#pragma once
#include"HashTable.h"
namespace KeyBreak
{
template<class K, class Hash = HashFuc<K>>
class Unorderedset
{
struct KeyofSet
{
const K& operator()(const K& Key)
{
return Key;
}
};
public:
typedef typename hash_bucket::HashTable<K,const K,KeyofSet,Hash>::ITERATOR iterator;
typedef typename hash_bucket::HashTable<K,const K,KeyofSet,Hash>::CONSTITERATOR const_iterator;
pair<iterator, bool> insert(const K& Key)
{
return _table.Insert(Key);
}
iterator begin()
{
return _table.begin();
}
iterator end()
{
return _table.end();
}
const_iterator begin()const
{
return _table.begin();
}
const_iterator end()const
{
return _table.end();
}
private:
hash_bucket::HashTable<K, const K, KeyofSet,Hash> _table;
};
void test01()
{
int a[] = { 1,2,3,4,5,6,7 };
Unorderedset<int> T;
for (auto e : a)
{
T.insert({ e});
}
Unorderedset<int>::iterator it = T.begin();
while (it != T.end())
{
cout << *it << " ";
++it;
}
cout << endl;
for (auto e : T)
{
cout << e << " ";
}
const Unorderedset<int> T2(T);
for (auto e : T2)
{
cout << e << " ";
}
}
}
3.2 Unorderedmap.h
cpp
#pragma once
#include"HashTable.h"
namespace KeyBreak
{
template<class K,class V, class Hash = HashFuc<K>>
class Unorderedmap
{
struct KeyofMap
{
const K& operator()(const pair<K,V>& KV)
{
return KV.first;
}
};
public:
typedef typename hash_bucket::HashTable<K, pair<const K, V>, KeyofMap,Hash>::ITERATOR iterator;
typedef typename hash_bucket::HashTable<K, pair<const K, V>, KeyofMap,Hash>::CONSTITERATOR const_iterator;
pair<iterator, bool> insert(const pair<K,V>&KV)
{
return _table.Insert(KV);
}
iterator begin()
{
return _table.begin();
}
iterator end()
{
return _table.end();
}
const_iterator begin()const
{
return _table.begin();
}
const_iterator end()const
{
return _table.end();
}
V& operator[](const K& Key)
{
pair<iterator, bool> ret = _table.Insert({ Key,V() });
return ret.first->second;
}
private:
hash_bucket::HashTable<K, pair<const K,V>, KeyofMap,Hash> _table;
};
void test02()
{
Unorderedmap<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;
//Unorderedmap<string, string>::iterator it = dict.begin();
//while (it != dict.end())
//{
// // 不能修改first,可以修改second
// //it->first += 'x';
// it->second += 'x';
// cout << it->first << ":" << it->second << endl;
// ++it;
//}
cout << endl;
}
}
3.3 HashTable.h
cpp
#pragma once
#include<vector>
#include<iostream>
using namespace std;
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;
}
template<class K>
struct HashFuc
{
size_t operator()(const K& Key)
{
return (size_t)Key;
}
};
template<>
struct HashFuc<string>
{
size_t operator()(const string& s)
{
size_t hash0 = 0;
for (auto e : s)
{
hash0 += e;
hash0 *= 131;
}
return hash0;
}
};
namespace hash_address
{
enum State
{
EMPTY,
DELETE,
EXIT
};
template<class K, class V>
struct HashNode
{
pair<K, V> _kv;
State _state;
HashNode()
:_state(EMPTY)
{
}
};
template<class K, class V, class Hash = HashFuc<K>>
class HashTable
{
typedef HashNode<K, V> Node;
public:
HashTable()
:_table(__stl_next_prime(0))
, _n(0)
{
}
bool Insert(const pair<K, V>& KV)
{
Hash hast;
if(Find(KV.first))
{
return false;
}
if (_n / (double)_table.size() >= 0.7)
{
HashTable<K,V,Hash> newtable;
newtable._table.resize(__stl_next_prime(_table.size()+1));
for (auto e : _table)
{
newtable.Insert(e._kv);
}
_table.swap(newtable._table);
}
size_t hash0 = hast(KV.first) % (_table.size());
size_t hashi = hash0;
int i = 1;
while (_table[hashi]._state == EXIT)
{
hashi = (hash0 + i) % (_table.size());
++i;
}
_table[hashi]._kv.first = KV.first;
_table[hashi]._kv.second = KV.second;
_table[hashi]._state = EXIT;
_n++;
return true;
}
Node* Find(const K& Key)
{
Hash hast;
size_t hash0 = hast(Key) % (_table.size());
size_t hashi = hash0;
int i = 0;
while (_table[hashi]._state == EXIT)
{
if (_table[hashi]._state != EMPTY && _table[hashi]._kv.first == Key)
{
return &_table[hashi];
}
hashi = (hash0 + i) % (_table.size());
++i;
}
return nullptr;
}
bool Erase(const K& Key)
{
Node* cur = Find(Key);
if (cur)
{
cur->_state = DELETE;
_n--;
return true;
}
else
{
return false;
}
}
private:
vector<Node> _table;
size_t _n;
};
}
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> table;
typedef HTIterator<K, T, Ref, Ptr, KeyofT, Hash> Self;
Node* _node;
const table* _tb;
HTIterator(Node* node, const table* tb)//要const修饰
:_node(node)
, _tb(tb)
{
}
T& operator*()
{
return _node->_data;
}
T* operator->()
{
return &_node->_data;
}
bool operator==(const Self& tmp)
{
return _node == tmp._node;
}
bool operator!=(const Self& tmp)
{
return _node != tmp._node;
}
Self& operator++()
{
KeyofT kot;
Hash hst;
if (_node->_next)
{
_node = _node->_next;
}
else
{
size_t hash = hst(kot(_node->_data)) % _tb->_table.size();//私有成员
++hash;
while (hash < _tb->_table.size())
{
_node = _tb->_table[hash];
if (_node)
{
break;
}
else
{
hash++;
}
}
if (hash == _tb->_table.size())
{
_node = nullptr;
}
}
return *this;
}
};
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 < _table.size(); i++)
{
if (_table[i])
{
return { _table[i],this };
}
}
return end();
}
ITERATOR end()
{
return { nullptr,this };
}
CONSTITERATOR begin()const
{
if (_n == 0)
{
return end();
}
for (size_t i = 0; i < _table.size(); i++)
{
if (_table[i])
{
return { _table[i],this };
}
}
return end();
}
CONSTITERATOR end()const
{
return { nullptr,this };
}
HashTable()
:_table(11)
, _n(0)
{
}
pair<ITERATOR,bool> Insert(const T& data)
{
KeyofT kot;
Hash hst;
ITERATOR ret = Find(kot(data));
if (ret!=end())
{
return { ret,false};
}
if (_n / (double)_table.size() == 1)
{
vector<Node*> newtable(2*_table.size());
for (size_t i = 0; i < _table.size(); i++)
{
Node* cur = _table[i];
while (cur)
{
Node* next = cur->_next;
size_t hash0 = hst(kot(cur->_data)) % newtable.size();
cur->_next = newtable[hash0];
newtable[hash0] = cur;
cur = next;
}
_table[i] = nullptr;
}
_table.swap(newtable);
}
size_t hash0 = hst(kot(data)) % _table.size();
Node* newnode = new Node(data);
newnode->_next = _table[hash0];
_table[hash0] = newnode;
_n++;
return { ITERATOR(newnode,this),true};
}
ITERATOR Find(const K& Key)
{
KeyofT kot;
Hash hst;
size_t hash0 = hst(Key) % _table.size();
Node* cur = _table[hash0];
while (cur)
{
if (kot(cur->_data) == Key)
{
return ITERATOR(cur,this);
}
cur = cur->_next;
}
return ITERATOR(nullptr,this);
}
bool Erase(const K& Key)
{
KeyofT kot;
Hash hst;
size_t hash0 = hst(Key) % _table.size();
Node* cur = _table[hash0];
Node* pre = nullptr;
while (cur)
{
if (kot(cur->_data) == Key)
{
if (pre == nullptr)
{
_table[hash0] = cur->_next;
}
else
{
pre->_next = cur->_next;
}
delete cur;
_n--;
return true;
}
else
{
pre = cur;
cur = cur->_next;
}
}
return false;
}
private:
vector<Node*> _table;
size_t _n;
};
}
文章结语
感谢你读到这里~我是「键盘敲碎了雾霭」,愿这篇文字帮你敲开了技术里的小迷雾 💻
如果内容对你有一点点帮助,不妨给个暖心三连吧👇
👍 点赞 | ❤️ 收藏 | ⭐ 关注
(听说三连的小伙伴,代码一次编译过,bug绕着走~)
你的支持,就是我继续敲碎技术雾霭的最大动力 🚀
🐶 小彩蛋:
/^ ^\
/ 0 0 \
V\ Y /V
/ - \
/ |
V__) ||
摸一摸毛茸茸的小狗,赶走所有疲惫和bug~我们下篇见 ✨