文章目录
- 📖 前言
 - 1. 复用同一个哈希桶⚡
 - 1.1 🌀修改后结点的定义
 - 1.2 🌀两个容器各自模板参数类型:
 
- 2. 改造之后的哈希桶⛳
 - 3. 哈希桶的迭代器🔥
 - 3.1 💥哈希桶的begin()和 end()的定义
 - 3.2 💥 operator* 和 operator->
 - 3.3 💥 operator++
 - 3.4 💥 operator== 和 operator!=
 
- 4. 封装unordered_map和unordered_set⭕
 
📖 前言
与学习红黑树和map、set的思路一样,我们在学unordered_map和unordered_set时,也是先学底层结构,在用模拟的底层结构来自己封装一下该容器,动手实践来让我们更好的学习和理解底层逻辑。
前情回顾:哈希桶 👉 传送门
这里用到的封装思路和封装map、set的思路相同,都是更高维度的泛型编程。
思路复习:封装map和set 👉 传送门
1. 复用同一个哈希桶⚡
如何复用同一个哈希桶,我们就需要对哈希桶进行改造,将哈希桶改造的更加泛型一点,既符合Key模型,也符合Key_Value模型。
1.1 🌀修改后结点的定义

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

 如何取到想要的数据:
- 我们给每个容器配一个仿函数
 - 各传不同的仿函数,拿到想要的不同的数据
 
同时我们再给每个容器配一个哈希函数。
2. 改造之后的哈希桶⛳
//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;
};
 
研究表明:除留余数法,最好模一个素数
- 通过查STL官方库我们也发现,其提供了一个取素数的函数
 - 所以我们也提供了一个,直接拷贝过来
 -  
  
- 这样我们在扩容时就可以每次给素数个桶
 
 -  
  
- 在扩容时加了一条判断语句是为了防止素数值太大,过分扩容容易直接把空间(堆)干崩了
 
 
3. 哈希桶的迭代器🔥
3.1 💥哈希桶的begin()和 end()的定义

- 以第一个桶中第一个不为空的结点为整个哈希桶的开始结点
 - 以空结点为哈希桶的结束结点
 
3.2 💥 operator* 和 operator->

 同之前operator->的连续优化一样,不再赘述……
3.3 💥 operator++

 备注:
- 这里要在哈希桶的类外面访问其私有成员
 - 我们要搞一个友元类
 - 迭代器类是哈希桶类的朋友
 - 这样就可以访问了
 

思路:
- 判断一个桶中的数据是否遍历完
 -  
  
- 如果所在的桶没有遍历完,在该桶中返回下一个结点指针
 
 -  
  
- 如果所在的桶遍历完了,进入下一个桶
 
 - 判断下一个桶是否为空
 -  
  
- 非空返回桶中第一个节点
 
 -  
  
- 空的话就遍历一个桶
 
 
后置++和之前一眼老套路,不赘述
注意:
- unordered_map和unordered_set是不支持反向迭代器的,从底层结构我们也能很好的理解(单链表找不了前驱)
 - 所以不支持实现迭代器的operator- -
 
3.4 💥 operator== 和 operator!=

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;
	}
};
 

 编译器的原则是向上查找(定义必须在前面,否则必须先声明)
4. 封装unordered_map和unordered_set⭕
有了上面的哈希桶的改装,我们这里的对map和set的封装就显得很得心应手了。
unordered_map的封装:
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;
};
 
这里unordered_map中的operator[ ]我们知道其原理之后,模拟实现就非常方便,直接调用插入函数,控制好参数和返回值即可。
对unordered_set的封装:
template<class K, class HashFunc = DefaultHash<K>>
class unordered_set
{
	//SteKeyOfT是set专用的就用内部类
	struct SetKeyOfT
	{
		const K& operator()(const K& key)
		{
			return key;
		}
	};
public:
	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;
};
                


















