【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map

文章目录

  • 前言
  • 一.哈希表模板改造+封装unordered_set和unordered_map
    • 1. 哈希表结构修改
    • 2. unordered_set和unordered_map增加KeyOfT仿函数
    • 3. insert封装及测试
    • 4. 哈希表迭代器的实现
    • 5. begin和end
    • 6. unordered_set和unordered_map的迭代器封装
    • 7. unordered_map的[]重载
    • 8. 补充完善:find、erase
    • 9. 存储自定义类型元素
    • 10. const迭代器的实现及unordered_set元素可以修改问题的解决
    • 11. unordered_map const迭代器的封装
  • 二. 源码
    • 1. HashTable.h
    • 2. UnorderedSet.h
    • 3. UnorderedMap.h
    • 4. Test.cpp

前言

前面的文章我们学习了unordered_set和unordered_map的使用以及哈希表,并且我们提到了unordered_set和unordered_map的底层结构其实就是哈希表。
那这篇文章我们就对之前我们实现的哈希表(拉链法实现的那个)进行一个改造,并用它模拟实现一下unordered_set和unordered_map。

那在模拟实现之前要声明一下:

我们这里的模拟实现里面所做的操作和前面红黑树模拟实现mapset基本上是一样的,增加和改造的那些模板参数的意义基本都是一样的。
所以这里有些地方我们就不会特别清楚的去说明了,如果某些地方大家看的不能太明白,建议先搞懂这篇文章——使用红黑树模拟实现STL中的map与set
这里面我们是讲的比较清楚的。

一.哈希表模板改造+封装unordered_set和unordered_map

首先可以带大家再来简单看一下库里面的哈希表的源码:

【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第1张图片
我们来看一下这几个模板参数
第一个value就决定了哈希表里面每个data里面存的数据类型,第二个参数key就是用来获取单独的键值key,因为unordered_map进行查找这些操作的时候是用key进行散列的,需要比较的话也是用key,但他里面存的是pair。
第三个这个HashFcn就是接收一个仿函数,用来将比如字符串这些类型转换为整型的。
第四个的作用就和红黑树封装那里的KeyOfT一样,用来提取key的。
那我们先看这么多。

接下来我们对我们的拉链法的哈希表进行一些改造,因为我们当时是按照KV模型实现的,而现在要变成通用的。

1. 哈希表结构修改

首先结点的结构要改一下:

【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第2张图片
这样对于unordered_setT就是单独一个key,对于unordered_map就是一个pair。

然后哈希表的结构:

【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第3张图片
之前Node里面是KV,现在由T决定结点里面存什么
那下面相关的地方都要改一下
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第4张图片
那大家看这个地方是不是就需要使用keyOfT那个仿函数了
因为data有可能是单独一个key,也有可能是一个pair,而像查找这些地方要使用Key去查找。
增加一个模板参数
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第5张图片

2. unordered_set和unordered_map增加KeyOfT仿函数

然后我们把unordered_set/map能写的先写一写:

【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第6张图片
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第7张图片

3. insert封装及测试

那我们先把insert搞一下,然后测试一下

【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第8张图片
unordered_map
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第9张图片

测试一下:

unordered_set的插入
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第10张图片
没问题
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第11张图片
然后,unordered_map的插入
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第12张图片
没问题。

4. 哈希表迭代器的实现

接着我们来实现一下哈希表的迭代器

我们来思考一下它的迭代器应该怎么搞:

那按照我们以往的经验,它的迭代器应该还是对结点指针的封装,然后顺着每个不为空的哈希桶(链表)进行遍历就行了。
那大家思考一下:
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第13张图片
比如现在底层的哈希表是这样的,it在2这个结点的位置。
那++it怎么走?
,其实很简单嘛,node->next不为空,就直接走到下一个结点就行了。
那如果为空呢?比如走到1002这个结点
如果node->next为空,就说明当前这个桶遍历完了,所以就往后寻找下一个不为空的桶,然后it指向这个桶的第一个结点。
库里面其实也是这样搞的。
所以,对于哈希表的迭代器来说,还是结点指针的封装,但是还要包含另一个成员即哈希表。
因为我们遍历哈希表去依次找桶。
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第14张图片

那我们自己来实现一下

先定义一下结构:

【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第15张图片

然后常用成员函数我们实现一下:

【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第16张图片
这些都没什么难度

然后++我们实现一下:

思路我们上面分析过了,来写一下代码
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第17张图片

5. begin和end

迭代器搞的差不多,我们把begin和end搞一下

首先begin是返回第一个位置的迭代器,那第一个位置怎么找?
,是不是第一个非空的哈希桶的第一个结点啊
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第18张图片
注意我们这里的迭代器的构造
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第19张图片
是用结点的指针和表的指针,而this就是当前哈希表的指针。
然后end用空构造就行了
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第20张图片

6. unordered_set和unordered_map的迭代器封装

那哈希表的迭代器实现好,我们就可以封装unordered_set和unordered_map的迭代器了

先来unordered_set:

【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第21张图片
来试一下
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第22张图片
报了好多错误。

我们来解决一下:

【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第23张图片
第一个迭代器的类里面无法访问私有成员_table
那解决方法呢我们可以写Get方法,当然这里也可以用友元解决。
那我们用一下友元吧
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第24张图片
然后再运行
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第25张图片
就可以了。
当然范围for肯定也支持了。
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第26张图片

那unordered_set搞好了,unordered_map的迭代器我们也来封装一下:

【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第27张图片
来测试一下
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第28张图片
没有问题。

然后我们可以再用统计次数那个测试一下:

【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第29张图片
但是我们还没给unordered_map重载[]

7. unordered_map的[]重载

来写一下:

那通过前面的学习我们知道要实现[]的话,关键在于insert的返回值。
insert要返回一个pair
插入有成功和失败两种情况,因为键值不允许冗余;它返回的是一个pair,第一个模板参数为迭代器,第二个为bool。
当插入成功的时候,pair的first为指向新插入元素的迭代器,second为true,当插入失败的时候(其实就是插入的键已经存在了),那它的first为容器中已存在的那个相同的等效键元素的迭代器,second为false。
所以后面这个bool其实是标识插入成功还是失败。

这都是我们前面讲过的。

那我们前面实现的insert返回bool,所以我们要修改一下:

首先find我们先改一下,我们之前返回NOde*,现在应该返回对应位置的迭代器
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第30张图片
然后insert返回一个pair
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第31张图片
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第32张图片
还有unordered_map/set里面的insert我们也改一下
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第33张图片
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第34张图片

然后我们给unordered_map封装一个[]:

【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第35张图片

再来测试:

【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第36张图片
就可以了。

8. 补充完善:find、erase

unordered_set和unordered_map的find和erase我们也搞一下吧,其实就是套一层壳嘛:

【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第37张图片

9. 存储自定义类型元素

如果我们现在想让unordered_map里面的key为日期类

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
	bool operator<(const Date& d)const
	{
		return (_year < d._year) ||
			(_year == d._year && _month < d._month) ||
			(_year == d._year && _month == d._month && _day < d._day);
	}
	bool operator>(const Date& d)const
	{
		return (_year > d._year) ||
			(_year == d._year && _month > d._month) ||
			(_year == d._year && _month == d._month && _day > d._day);
	}
	friend ostream& operator<<(ostream& _cout, const Date& d);
private:
	int _year;
	int _month;
	int _day;
};
ostream& operator<<(ostream& _cout, const Date& d)
{
	_cout << d._year << "-" << d._month << "-" << d._day;
	return _cout;
}

我们写这样一段代码:

【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第38张图片
我们现在直接运行肯定是有问题的
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第39张图片
因为date类的数据无法转换成整型,所以我们要传那个keyToInt的仿函数。

那我们的哈希表是有这个模板参数的,但是我们现在得给unordered_set和unordered_map增加这个模板参数:

【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第40张图片
那哈希表里面这个缺省参数我们就不用传了。
而是搞到unordered_set和unordered_map这里
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第41张图片
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第42张图片

现在我们增加了这个模板参数,也有缺省值,但是对于date还是不行,因为它无法转换为整型,所以我们要自己写一个针对date类的仿函数:

【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第43张图片
但是这里类外无法访问date的私有成员,那我们还用友元解决吧
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第44张图片
然后我们也可以用个BKDR哈希
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第45张图片

然后我们再运行

【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第46张图片
会发现这里又有一个报错。
是因为我们的日期类没有重载==,我们加一下
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第47张图片
然后再运行
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第48张图片
就没问题了

另外呢我们看到:

最开始的时候带大家看库里面的源码,它的哈希表还有一个模板参数我们没有介绍
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第49张图片
就是这个EqualKey,他其实就是接收一个仿函数或函数指针去实现比较key是否相等的,必要的时候我们可以自己传。
我们这里没有用,因为这里的date类我们可以自己手动给它重载==,但是如果一个类不支持但是我们不能修改它或者某种类型的key可以比较==,但是不是我们想要的,比如date*,那像这种情况我们就可以写一个仿函数去传这个参数,不过我们这里没加

10. const迭代器的实现及unordered_set元素可以修改问题的解决

还有一个问题就是我们的unordered_set里面的元素现在是可以修改的,但是正常情况key是不能修改的,而且他是哈希函数里面的操作数,随意改散列就出问题了:

【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第50张图片

那我们来处理一下:

那其实解决方法和set那里是一样的,库里面也是一样的方法,让unordered_set的迭代器都是哈希表的const迭代器。

那首先我们得实现一下const迭代器:

先得给哈希表实现一下,还是之前的方法,通过增加两个模板参数
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第51张图片
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第52张图片
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第53张图片

然后const版本的begin和end:

【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第54张图片

那然后我们把set的迭代器重新封装一下:

【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第55张图片

然后再运行:

【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第56张图片
就不能修改了
但是
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第57张图片
又有了新的问题。
那这个问题其实还是跟我们之前封装mapset那里一样,因为这里不支持普通迭代器构造const迭代器
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第58张图片
所以要解决的话加一个可以支持普通迭代器构造const迭代器的构造函数就行了
同样的方法
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第59张图片
然后
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第60张图片
就可以了。

如果大家有地方看不太懂的还是建议去看一下之前红黑树模拟实现map与set那里,那里讲的比较仔细,它们的逻辑是一样的。

11. unordered_map const迭代器的封装

而对于unordered_map:

它的普通迭代器就是普通迭代器,const迭代器器就是const迭代器,区别在于value是否可以修改,而key是无论如何不能修改的
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第61张图片
这个通过传模板参数就可以控制。

那我们把它的const迭代器也搞一下吧:

在这里插入图片描述
const版本的begin和end:
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第62张图片

来试一下:

【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第63张图片
,我们看到普通迭代器可以修改value
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第64张图片
key是不行的。
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第65张图片
如果换成const迭代器,value也不能修改。
【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第66张图片
key肯定依然不能修改。

二. 源码

1. HashTable.h

#pragma once
#include 

template<class T>
struct HashNode
{
	T _data;
	HashNode<T>* _next;

	HashNode(const T& data)
		:_data(data)
		, _next(nullptr)
	{}
};

template<class K>
struct keyToIntFunc
{
	size_t operator()(const K& key)
	{
		return key;
	}
};

//对string类型进行特化
template<>
struct keyToIntFunc<string>
{
	size_t operator()(const string& key)
	{
		size_t sum = 0;
		for (auto& e : key)
		{
			sum = sum * 31 + e;
		}
		return sum;
	}
};

namespace HashBucket
{

	//前置声明,__HashIterator在哈希表上面,但里面用了哈希表
	template<class K, class T, class keyOfT, class keyToInt>
	class HashTable;

	template<class K, class T, class Ref, class Ptr, class keyOfT, class keyToInt>
	struct __HashIterator
	{
		typedef HashNode<T> Node;
		typedef HashTable<K, T, keyOfT, keyToInt> HashTable;
		typedef __HashIterator<K, T, Ref, Ptr, keyOfT, keyToInt> self;
		typedef __HashIterator<K, T, T&, T*, keyOfT, keyToInt> Iterator;

		Node* _node;
		HashTable* _ht;

		__HashIterator(Node* node, HashTable* ht)
			:_node(node)
			, _ht(ht)
		{}

		__HashIterator(const Iterator& it)
			:_node(it._node)
			, _ht(it._ht)
		{}

		Ref operator*()
		{
			return _node->_data;
		}

		Ptr operator->()
		{
			return &_node->_data;
		}

		bool operator!=(const self& it)
		{
			return _node != it._node;
		}

		self& operator++()
		{
			if (_node->_next != nullptr)
			{
				_node = _node->_next;
			}
			else//向后寻找非空的桶
			{
				//计算当前桶的位置
				size_t hashi = keyToInt()(keyOfT()(_node->_data)) % _ht->_table.size();
				//++走到下一个桶
				++hashi;
				while (hashi < _ht->_table.size())
				{
					//不为空
					if (_ht->_table[hashi])
					{
						_node = _ht->_table[hashi];
						break;
					}
					else
					{
						++hashi;
					}
				}
				//如果hashi == _ht->_table.size(),说明走到结束后面也没有非空桶
				if (hashi == _ht->_table.size())
				{
					_node = nullptr;
				}
			}
			return *this;
		}

	};

	template<class K, class T, class keyOfT,class keyToInt>
	class HashTable
	{
		template<class K, class T, class Ref, class Ptr, class keyOfT, class keyToInt>
		friend struct __HashIterator;

		typedef HashNode<T> Node;
	public:
		typedef __HashIterator<K, T, T&, T*, keyOfT, keyToInt> iterator;
		typedef __HashIterator<K, T, const T&, const T*, keyOfT, keyToInt> const_iterator;


		iterator begin()
		{
			Node* cur = nullptr;
			for (size_t i = 0; i < _table.size(); i++)
			{
				if (_table[i])
				{
					cur = _table[i];
					break;
				}
			}
			return iterator(cur, this);
		}

		iterator end()
		{
			return iterator(nullptr, this);
		}

		const_iterator begin()const
		{
			Node* cur = nullptr;
			for (size_t i = 0; i < _table.size(); i++)
			{
				if (_table[i])
				{
					cur = _table[i];
					break;
				}
			}
			return const_iterator(cur, this);
		}

		const_iterator end()const
		{
			return const_iterator(nullptr, this);
		}

		~HashTable()
		{
			for (auto& cur : _table)
			{
				while (cur)
				{
					Node* next = cur->_next;
					delete cur;
					cur = next;
				}
				cur = nullptr;
			}
		}

		size_t GetNextPrime(size_t prime)
		{
			// SGI
			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
			};

			size_t i = 0;
			for (; i < __stl_num_primes; ++i)
			{
				if (__stl_prime_list[i] > prime)
					return __stl_prime_list[i];
			}

			return __stl_prime_list[i];
		}

		pair<iterator, bool> Insert(const T& data)
		{
			iterator it = Find(keyOfT()(data));
			if (it != end())
			{
				return make_pair(it, false);
			}

			//负载因子==1进行扩容
			if (_n == _table.size())
			{
				//size_t newsize = _table.size() == 0 ? 10 : _table.size() * 2;
				size_t newsize = GetNextPrime(_table.size());

				vector<Node*> newtable(newsize, nullptr);
				for (auto& cur : _table)
				{
					while (cur)
					{
						Node* next = cur->_next;
						size_t hashi = keyToInt()(keyOfT()(cur->_data)) % newtable.size();

						//把结点头插到新表
						cur->_next = newtable[hashi];
						newtable[hashi] = cur;

						cur = next;
					}
				}
				_table.swap(newtable);
			}

			//计算散列地址
			size_t hashi = keyToInt()(keyOfT()(data)) % _table.size();

			//链到散列地址对应的单链表上(头插)
			Node* newNode = new Node(data);
			newNode->_next = _table[hashi];
			_table[hashi] = newNode;

			++_n;
			return make_pair(iterator(newNode, this), true);
		}

		iterator Find(const K& key)
		{
			if (_table.size() == 0)
			{
				return end();
			}
			size_t hashi = keyToInt()(key) % _table.size();
			Node* cur = _table[hashi];

			while (cur)
			{
				if (keyOfT()(cur->_data) == key)
				{
					return iterator(cur, this);
				}
				cur = cur->_next;
			}
			return end();
		}

		bool Erase(const K& key)
		{
			size_t hashi = keyToInt()(key) % _table.size();
			Node* prev = nullptr;
			Node* cur = _table[hashi];

			while (cur)
			{
				if (key == keyOfT()(cur->_data))
				{
					//头删
					if (prev == nullptr)
					{
						_table[hashi] = cur->_next;
					}
					//非头删
					else
					{
						prev->_next = cur->_next;
					}
					delete cur;
					return true;
				}
				else
				{
					prev = cur;
					cur = cur->_next;
				}
			}
			return false;
		}

		size_t MaxBucketSize()
		{
			size_t max = 0;
			for (auto& cur : _table)
			{
				size_t size = 0;
				while (cur)
				{
					++size;
					cur = cur->_next;
				}
				if (size > max)
				{
					max = size;
				}
			}
			return max;
		}

	private:
		vector<Node*> _table;
		size_t _n = 0;
	};
}

2. UnorderedSet.h

#pragma once

#include "HashTable.h"

template<class K, class KeyToInt = keyToIntFunc<K>>
class UnorderedSet
{
public:
	struct setKeyOfT
	{
		const K& operator()(const K& key)
		{
			return key;
		}
	};
	typedef typename HashBucket::HashTable<K, K, setKeyOfT, KeyToInt>::const_iterator iterator;
	typedef typename HashBucket::HashTable<K, K, setKeyOfT, KeyToInt>::const_iterator 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()
	{
		return _ht.Find();
	}
	bool erase()
	{
		return _ht.Erase();
	}

private:
	HashBucket::HashTable<K, K, setKeyOfT, KeyToInt> _ht;
};

void UnorderedSet_test1()
{
	int arr[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15,106 };
	UnorderedSet<int> m;
	m.insert(1);
	m.insert(7);
	m.insert(5);
	m.insert(2);
	m.insert(8);
	for (auto e : arr)
	{
		m.insert(e);
	}

	UnorderedSet<int>::iterator it = m.begin();
	while (it != m.end())
	{
		//(*it)++;
		cout << *it << " ";
		++it;
	}
	cout << endl;
	for (auto e : m)
	{
		cout << e << " ";
	}
	cout << endl;

}

3. UnorderedMap.h

#pragma once

#include "HashTable.h"

template<class K, class V, class KeyToInt = keyToIntFunc<K>>
class UnorderedMap
{
public:
	struct MapKeyOfT
	{
		const K& operator()(const pair<K,V>& kv)
		{
			return kv.first;
		}
	};

	typedef typename HashBucket::HashTable<K, pair<const K, V>, MapKeyOfT, KeyToInt>::iterator iterator;
	typedef typename HashBucket::HashTable<K, pair<const K, V>, MapKeyOfT, KeyToInt>::const_iterator 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(make_pair(key, V()));
		return ret.first->second;
	}
	iterator find()
	{
		return _ht.Find();
	}
	bool erase()
	{
		return _ht.Erase();
	}
private:
	HashBucket::HashTable<K, pair<const K,V>, MapKeyOfT, KeyToInt> _ht;
};

void UnorderedMap_test1()
{
	UnorderedMap<int, int> m;
	m.insert(make_pair(1, 1));
	m.insert(make_pair(7, 7));
	m.insert(make_pair(5, 5));
	m.insert(make_pair(2, 2));
	m.insert(make_pair(8, 8));

	UnorderedMap<int, int>::const_iterator it = m.begin();
	while (it != m.end())
	{
		//it->first++;
		cout << it->first << ":" << it->second << " ";
		++it;
	}
	cout << endl;
}

void UnorderedMap_test2()
{
	string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜",
"苹果", "香蕉", "苹果", "香蕉" };

	UnorderedMap<string, int> m;
	for (auto e : arr)
	{
		m[e]++;
	}

	for (auto e : m)
	{
		cout << e.first << ":" << e.second << endl;
	}
}
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
	bool operator<(const Date& d)const
	{
		return (_year < d._year) ||
			(_year == d._year && _month < d._month) ||
			(_year == d._year && _month == d._month && _day < d._day);
	}
	bool operator>(const Date& d)const
	{
		return (_year > d._year) ||
			(_year == d._year && _month > d._month) ||
			(_year == d._year && _month == d._month && _day > d._day);
	}
	bool operator==(const Date& d)const
	{
		return _year == d._year
			&& _month == d._month
			&& _day == d._day;
	}
	friend ostream& operator<<(ostream& _cout, const Date& d);
	friend struct dateToInt;
private:
	int _year;
	int _month;
	int _day;
};
ostream& operator<<(ostream& _cout, const Date& d)
{
	_cout << d._year << "-" << d._month << "-" << d._day;
	return _cout;
}

struct dateToInt
{
	size_t operator()(const Date& d)
	{
		size_t ret = 0;
		ret += d._year;
		ret *= 31;
		ret += d._month;
		ret *= 31;
		ret += d._day;
		ret *= 31;

		return ret;
	}
};

void UnorderedMap_test3()
{
	Date d1(2023, 3, 13);
	Date d2(2023, 3, 13);
	Date d3(2023, 3, 12);
	Date d4(2023, 3, 11);
	Date d5(2023, 3, 12);
	Date d6(2023, 3, 13);
	Date a[] = { d1, d2, d3, d4, d5, d6 };

	UnorderedMap<Date, int, dateToInt> countMap;
	for (auto e : a)
	{
		countMap[e]++;
	}
	for (auto& kv : countMap)
	{
		cout << kv.first << ":" << kv.second << endl;
	}
}

4. Test.cpp

#define _CRT_SECURE_NO_WARNINGS

#include 
using namespace std;
#include 
#include "HashTable.h"
#include "UnorderedSet.h"
#include "UnorderedMap.h"
#include 
int main()
{
	//UnorderedSet_test1();
	UnorderedMap_test1();
	return 0;
}

【C++】使用哈希表模拟实现STL中的unordered_set和unordered_map_第67张图片

你可能感兴趣的:(C++,c++,散列表,开发语言)