【C++】用哈希表封装myunordered_map和 myunordered_set

目录

前言

接着【C++】哈希表的实现详情请点击,今天继续介绍【C++】用哈希表封装myunordered_map和 myunordered_set
本次myunordered_map和 myunordered_set的介绍是在学习了解了【C++】哈希表的实现详情请点击查看以及【C++】STL----封装红黑树实现map和set详情请点击查看的基础上展开介绍的

一、源码及框架分析

  • 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的实现结构框架核心部分
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(); }
};

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

// 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>

二、unordered_map和unordered_set的实现

  • 首先将哈希表的实现部分的哈希桶实现代码外层进行修改
  • 哈希节点的模板传入的参数不再是一个确定的class T,class V的结构,而是传入一个T类型(可能是key,也可能是pair类型的key/value)
cpp 复制代码
// 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
};

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;
	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;
	}
};
//string特化
template<>
struct HashFunc<string>
{
	size_t operator()(const string& key)
	{
		size_t hashi = 0;
		for (auto& ch : key)
		{
			hashi *= 131;
			hashi += ch;
		}
		return hashi;
	}
};

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 Hash = HashFunc<K>>
	class HashTable
	{
		typedef HashNode<T> Node;
	public:
		HashTable(size_t size = __stl_next_prime(0))
			:_tables(size, nullptr)
		{
		}
		~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;
			}
		}
		bool Insert(const pair<K, V>& kv)
		{
			if (Find(kv.first))
				return false;
			Hash hs;
			//负载因子为1再扩容
			if (_n == _tables.size())
			{
				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(cur->_kv.first) % newtables.size();
						cur->_next = newtables[hashi];
						newtables[hashi] = cur;

						cur = next;
					}
					_tables[i] = nullptr;
				}

				_tables.swap(newtables);
			}
			size_t hashi = hs(kv.first) % _tables.size();

			//头插
			Node* newnode = new Node(kv);

			newnode->_next = _tables[hashi];
			_tables[hashi] = newnode;
			++_n;
			return true;
		}
		Node* Find(const K& key)
		{
			Hash hs;
			size_t hashi = hs(key) % _tables.size();
			Node* cur = _tables[hashi];
			while (cur)
			{
				if (cur->_kv.first == key)
					return cur;
				cur = cur->_next;
			}
			return nullptr;
		}
		bool Erase(const K& key)
		{
			Hash hs;
			size_t hashi = hs(key) % _tables.size();
			Node* cur = _tables[hashi];
			Node* prev = nullptr;
			while (cur)
			{
				if (cur->_kv.first == key)
				{
					if (prev == nullptr)
					{
						_tables[hashi] = cur->_next;
					}
					else
					{
						prev->_next = cur->_next;
					}
					--_n;
					delete cur;
					return true;
				}
				prev = cur;
				cur = prev->_next;
			}
			return false;
		}
	private:
		vector<Node*> _tables; //指针数组
		size_t _n = 0;
	};
};
  • 添加UnorderedMap.h,UnorderedSet.h,并包含HashTable.h头文件,并将UnorderedMap,UnorderedSet框架搭出来
  • UnorderedMap,UnorderedSet底层就是依靠哈希桶实现的
cpp 复制代码
//UnorderedSet.h
namespace gy
{
	template<class K>
	class unordered_set
	{
	public:
	private:
		hash_bucket::HashTable<K, K> _ht;
	};
}
//UnorderedMap.h
namespace gy
{
	template<class K, class V>
	class unordered_map
	{
	public:
	private:
		hash_bucket::HashTable<K, pair<K, V>> _ht;
	};
}

insert

  • UnorderedSet和UnorderedMap的insert实现和map/set一样外层并没有具体实现方法,而是调用底层Insert算法。
cpp 复制代码
//UnorderedSet.h
namespace gy
{
	template<class K>
	class unordered_set
	{
	public:
		bool insert(const K& key)
		{
			_ht.Insert(key);
		}
	private:
		hash_bucket::HashTable<K, K> _ht;
	};
}
//UnorderedMap.h
namespace gy
{
	template<class K, class V>
	class unordered_map
	{
	public:
		bool insert(const pair<K, V>& kv)
		{
			_ht.Insert(kv);
		}
	private:
		hash_bucket::HashTable<K, pair<K, V>> _ht;
	};
}
  • 修改哈希桶的Insert算法,现在我们传入的数据类型不确定了,因此Insert的实现上也需要进行相应的修改
  • 从上面Insert的代码中我们可以发现,Insert中有Find函数需要key值,且插入哈希桶位置的计算也需要key值,但是我们现在传入参数有可能是key,也可能是pair类型,因此我们需要将key值提取出来
  • 在map/set的实现过程中也遇到过同样的问题,因此我们还是采用map和set层分别实现一个MapKeyOfT和SetKeyOfT的仿函数传给RBTree的KeyOfT,然后RBTree中通过KeyOfT仿函数取出T类型对象中的key,再进行比较的方法,来实现去除T类型中的key
cpp 复制代码
//UnorderedSet.h
namespace gy
{
	template<class K>
	class unordered_set
	{
	public:
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
		bool insert(const K& key)
		{
			_ht.Insert(key);
		}
	private:
		hash_bucket::HashTable<K, K, SetKeyOfT> _ht;
	};
}
//UnorderedMap.h
namespace gy
{
	template<class K, class V>
	class unordered_map
	{
	public:
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
		bool insert(const pair<K, V>& kv)
		{
			_ht.Insert(kv);
		}
	private:
		hash_bucket::HashTable<K, pair<K, V>, MapKeyOfT> _ht;
	};
}
  • 同时Find函数也是一样需要将key值取出进行比较查找
cpp 复制代码
bool Insert(const T& data)
{
	KeyOfT kot;
	if(Find(kot(data)))
		return false;
	Hash hs;
	//负载因子为1再扩容
	if (_n == _tables.size())
	{
		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 true;
}

Node* Find(const K& key)
{
	Hash hs;
	KeyOfT kot;
	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;
}

最终运行结果如下图所示

支持iterator、const_iterator的实现

  • iterator实现的大框架跟list的iterator思路是一致的,用一个类型封装结点的指针,再通过重载运算符实现,迭代器像指针一样访问的行为,要注意的是哈希表的迭代器是单向迭代器
cpp 复制代码
//源码
template <class Value, class Key, class HashFcn, class ExtractKey, class EqualKey, class Alloc>

struct __hashtable_iterator {
	 typedef hashtable<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc>
	 hashtable;
	 typedef __hashtable_iterator<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc> iterator;
	 typedef __hashtable_const_iterator<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc> const_iterator;
	 typedef __hashtable_node<Value> node;
	 typedef forward_iterator_tag iterator_category;
	 typedef Value value_type;
	 node* cur;
	 hashtable* ht;
 __hashtable_iterator(node* n, hashtable* tab) : cur(n), ht(tab) {}
 __hashtable_iterator() {}
 reference operator*() const { return cur->val; }
#ifndef __SGI_STL_NO_ARROW_OPERATOR
	 pointer operator->() const { return &(operator*()); }
#endif /* __SGI_STL_NO_ARROW_OPERATOR */
	 iterator& operator++();
	 iterator operator++(int);
	 bool operator==(const iterator& it) const { return cur == it.cur; }
	 bool operator!=(const iterator& it) const { return cur != it.cur; }
};
template <class V, class K, class HF, class ExK, class EqK, class A>
__hashtable_iterator<V, K, HF, ExK, EqK, A>& __hashtable_iterator<V, K, HF, ExK, EqK, A>::operator++()
{
	 const node* old = cur;
	 cur = cur->next;
	 if (!cur) {
		 size_type bucket = ht->bkt_num(old->val);
		 while (!cur && ++bucket < ht->buckets.size())
		 cur = ht->buckets[bucket];
	 }
	 return *this;
}

iterator实现

  • operator++的实现。iterator中有一个指向结点的指针,如果当前桶下面还有结点,
    则结点的指针指向下⼀个结点即可。如果当前桶走完了,则需要想办法计算找到下一个桶 。这里的难点是反而是结构设计的问题,参考上面的源码,我们可以看到iterator中除了有结点的指针,还有哈希表对象的指针,这样当前桶走完了,要计算下一个桶就相对容易多了,用key值计算出当前桶位置,依次往后找下一个不为空的桶即可
  • 同时需要注意,编译器编译的时候只会向上查找,HTIterator使用了HashTable,因此需要在HTIterator前声明HashTable
  • 在类外面,无法访问到类的私有成员变量,我们可以实现一个get函数来获得私有成员变量,还有一种方法就是友元,模板声明友元需要将模板参数带上
  • begin()返回第一个桶中第一个节点指针构造的迭代器,这里end()返回迭代器可以用空表示
cpp 复制代码
//HashTable.h
	//前置声明
	template<class K, class T, class KeyOfT, class Hash>
	class HashTable;

	template<class K, class T, class KeyOfT, class Hash>
	struct HTIterator
	{
		typedef HashNode<T> Node;
		typedef HashTable<K, T, KeyOfT, Hash> HT;
		typedef HTIterator<K, T, KeyOfT, Hash> Self;
		Node* _node;  //节点指针
		HT* _ht; //哈希表指针
		
		HTIterator(Node* node, HT* ht)
			:_node(node)
			,_ht(ht)
		{ }
		T& operator*()
		{
			return _node->_data;
		}
		T* operator->()
		{
			return &_node->_data;
		}
		Self& operator++()
		{
			if (_node->_next)
			{
				_node = _node->_next;

			}
			else
			{
				KeyOfT kot;
				Hash hs;
				size_t hashi = hs(kot(_node->_data)) % _ht->_tables.size();
				++hashi;
				while(hashi < _ht->_tables.size())
				{
					if (_ht->_tables[hashi])
					{
						_node = _ht->_tables[hashi];
						break;
					}
						
					++hashi;
				}
				if (hashi == _ht->_tables.size())
					_node = nullptr;
			}
			return *this;
		}
		bool operator != (const Self& s)
		{
			return _node != s._node;
		}
		bool operator == (const Self& s)
		{
			return _node == s._node;
		}
	};
template<class K, class T,class KeyOfT, class Hash = HashFunc<K>>
class HashTable
{
	template<class K, class T, class KeyOfT, class Hash>
	friend struct HTIterator;
	typedef HashNode<T> Node;
public:
	typedef HTIterator<K, T, KeyOfT, Hash> Iterator;
	Iterator Begin()
	{
		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);
	}
	//......
};
  • 注意:typedef取类模板中的内嵌类型时,由于类模板没有实例化,因此不确定是类型还是静态成员变量,因此需要加上typename ,还可以使用using关键字
cpp 复制代码
//UnorderedSet.h
namespace gy
{
	template<class K>
	class unordered_set
	{
	public:
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
		typedef typename hash_bucket::HashTable<K, K, SetKeyOfT> ::Iterator iterator;
		iterator begin()
		{
			return _ht.Begin();
		}
		iterator end()
		{
			return _ht.End();
		}
		bool insert(const K& key)
		{
			return _ht.Insert(key);
		}
	private:
		hash_bucket::HashTable<K, K, SetKeyOfT> _ht;
	};
}
//UnorderedMap.h
namespace gy
{
	template<class K, class V>
	class unordered_map
	{
	public:
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
		//typedef typename hash_bucket::HashTable<K, pair<K, V>, MapKeyOfT> ::Iterator iterator;
		using iterator = typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash> ::Iterator;
		iterator begin()
		{
			return _ht.Begin();
		}
		iterator end()
		{
			return _ht.End();
		}
		bool insert(const pair<K, V>& kv)
		{
			return _ht.Insert(kv);
		}
	private:
		hash_bucket::HashTable<K, pair<K, V>, MapKeyOfT> _ht;
	};
}

测试

cpp 复制代码
void test()
{
	gy::unordered_map<string, string> map;
	gy::unordered_set<int> set;
	int a[] = { 19, 30, 5, 36, 13, 20, 21, 12 };
	for (auto e : a)
		set.insert(e);
	map.insert({ "left", "左边" });
	map.insert({ "right", "右边" });
	map.insert({ "sort", "排序" });
	for (auto e : set)
		cout << e << " ";
	cout << endl;
	for (auto ch : map)
		cout << ch.first << " " << ch.second << endl;

}

const_iterator实现

  • 和list的const_iterator实现一样,我们需要在上面的iterator的基础上添加两个参数,Ref和Ptr来控制传入的类型是可修改的还是const修饰的不可修改的
cpp 复制代码
//HashTable.h
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;  //节点指针
	HT* _ht; //哈希表指针
	
	HTIterator(Node* node, HT* ht)
		:_node(node)
		,_ht(ht)
	{ }
	Ref operator*()
	{
		return _node->_data;
	}
	Ptr operator->()
	{
		return &_node->_data;
	}
	Self& operator++()
	{
		if (_node->_next)
		{
			_node = _node->_next;

		}
		else
		{
			KeyOfT kot;
			Hash hs;
			size_t hashi = hs(kot(_node->_data)) % _ht->_tables.size();
			++hashi;
			while(hashi < _ht->_tables.size())
			{
				if (_ht->_tables[hashi])
				{
					_node = _ht->_tables[hashi];
					break;
				}
					
				++hashi;
			}
			if (hashi == _ht->_tables.size())
				_node = nullptr;
		}
		return *this;
	}
	bool operator != (const Self& s)
	{
		return _node != s._node;
	}
	bool operator == (const Self& s)
	{
		return _node == s._node;
	}
};

template<class K, class T,class KeyOfT, class Hash = HashFunc<K>>
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> Const_Iterator;

	Iterator Begin()
	{
		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);
	}

	Const_Iterator Begin()const
	{
		for (size_t i = 0; i < _tables.size(); i++)
		{
			if (_tables[i])
				return Const_Iterator(_tables[i], this);
		}
		return End();
	}

	Const_Iterator End()const
	{
		return Const_Iterator(nullptr, this);
	}
	//....
};
  • 问题:上述代码编译报错,显示const迭代器没有重载函数可以转换的所有参数类型
  • 原因以及解决办法:迭代器初始化时传入的哈希表指针是HT*类型,但是const_iterator迭代器是被const修饰的(修饰的就是哈希表指针),const修饰的变量传给普通变量权限放大,因此编译无法通过。
  • 因此我们将迭代器哈希表的初始化使用const修饰,这样const和普通哈希表指针都能初始化
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> HT;
		typedef HTIterator<K, T, Ref, Ptr, KeyOfT, Hash> Self;

		Node* _node;  //节点指针
		const HT* _ht; //哈希表指针
		
		HTIterator(Node* node, const HT* ht)
			:_node(node)
			,_ht(ht)
		{ }
		//.....
	};

key不支持修改问题

  • unordered_set的iterator不支持修改,我们把unordered_set的第二个模板参数改成const K即可, HashTable<K, const K, SetKeyOfT, Hash> _ht;unordered_map的iterator不支持修改key但是可以修改value,我们把unordered_map的第二个模板参数pair的第一个参数改成const K即可, HashTable<K, pair<const K, V>, MapKeyOfT, Hash> _ht
cpp 复制代码
//UnorderedSet.h
namespace gy
{
	template<class K>
	class unordered_set
	{
	public:
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
		typedef typename hash_bucket::HashTable<K, const K, SetKeyOfT> ::Iterator iterator;
		typedef typename hash_bucket::HashTable<K, const K, SetKeyOfT> ::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();
		}
		bool insert(const K& key)
		{
			return _ht.Insert(key);
		}
	private:
		hash_bucket::HashTable<K, const K, SetKeyOfT> _ht;
	};
}
//UnorderedMap.h
namespace gy
{
	template<class K, class V>
	class unordered_map
	{
	public:
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
		typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT> ::Iterator iterator;
		typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT> ::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();
		}
		bool insert(const pair<K, V>& kv)
		{
			return _ht.Insert(kv);
		}
	private:
		hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT> _ht;
	};
}

map支持[]

  • map支持[],其本质底层是需要调用insert来实现,因此我们需要将我们上面的Insert函数进行修改,使其返回值是一个pair类型(pair<Iterator, bool>),因为Insert函数中使用了Find函数,因此Find函数也需要进行修改,返回值为Iterator类型
cpp 复制代码
//HashTable.h
pair<Iterator, bool> Insert(const T& data)
{
	KeyOfT kot;
	Iterator it = Find(kot(data));
	if(it != End())
	{
		return {it, false};
	}
	Hash hs;
	//负载因子为1再扩容
	if (_n == _tables.size())
	{
		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 { {newnode, this}, true };
}
Iterator Find(const K& key)
{
	Hash hs;
	KeyOfT kot;
	size_t hashi = hs(key) % _tables.size();
	Node* cur = _tables[hashi];
	while (cur)
	{
		if (kot(cur->_data)== key)
			return Iterator(cur, this);
		cur = cur->_next;
	}
	return End();
}
////UnorderedMap.h
V& operator[](const K& key)
{
	pair<iterator, bool> ret = _ht.Insert({key, V()});
	return ret.first->second;
}

测试

cpp 复制代码
void test2()
{
	gy::unordered_map<string, string> map;
	map.insert({ "left", "左边" });
	map.insert({ "right", "右边" });
	map.insert({ "sort", "排序" });
	map["123"]; //新插入
	map["left"]; 
	map["right"] = "右边+"; //修改
	gy::unordered_map<string, string>::iterator it2 = map.begin();
	while (it2 != map.end())
	{
		cout << it2->first << " " << it2->second << endl;
		++it2;
	}
	cout << endl;
}

key不可取模问题

  • 有一些key(日期类等)是不能直接取模的,如果我们在上层传入的K是一个Data类型,那么无法直接进行取模操作,因此我们需要修改代码,因为只有在上层实现传入仿函数,这样我们才能自己控制仿函数的实现
  1. 修改底层代码
cpp 复制代码
template<class K, class T,class KeyOfT, class Hash>
class HashTable
{
};
  1. 修改上层代码
cpp 复制代码
namespace gy
{
	template<class K, class Hash = HashFunc<K>>
	class unordered_set
	{
	private:
		hash_bucket::HashTable<K, const K, SetKeyOfT, Hash> _ht;
	};
}

namespace gy
{
	template<class K, class V, class Hash = HashFunc<K>>
	class unordered_map
	{
	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> ::Const_Iterator const_iterator;*/
		using iterator = typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash> ::Iterator;
		using const_iterator = typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash> ::Const_Iterator;
	private:
		hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, HashFunc<K>> _ht;
	};
}

Erase

  • Erase我们只需要将上一节哈希桶的代码小修改即可使用,由于_data类型不确定,因此使用KeyOfT 将key提取出来
cpp 复制代码
bool Erase(const K& key)
{
	Hash hs;
	KeyOfT kot;
	size_t hashi = hs(key) % _tables.size();
	Node* cur = _tables[hashi];
	Node* prev = nullptr;
	while (cur)
	{
		if (kot(cur->_data) == key)
		{
			if (prev == nullptr)
			{
				_tables[hashi] = cur->_next;
			}
			else
			{
				prev->_next = cur->_next;
			}
			--_n;
			delete cur;
			return true;
		}
		prev = cur;
		cur = prev->_next;
	}
	return false;
}
  • 上层直接调用底层Erase即可

三、完整代码

详情点击查看

相关推荐
a努力。2 小时前
字节Java面试被问:TCP的BBR拥塞控制算法原理
java·开发语言·python·tcp/ip·elasticsearch·面试·职场和发展
jiaguangqingpanda2 小时前
Day24-20260120
java·开发语言·数据结构
一个龙的传说3 小时前
xshell下载
java
C雨后彩虹3 小时前
羊、狼、农夫过河
java·数据结构·算法·华为·面试
xiaoye-duck3 小时前
C++ string 类使用超全攻略(上):创建、遍历及容量操作深度解析
c++·stl
java资料站3 小时前
SpringAI+DeepSeek大模型应用开发实战
java
Elastic 中国社区官方博客3 小时前
使用瑞士风格哈希表实现更快的 ES|QL 统计
大数据·数据结构·sql·elasticsearch·搜索引擎·全文检索·散列表
csdn_aspnet3 小时前
C++跨平台开发,分享一些用C++实现多平台兼容的工程难题与解决方案
c++
进击的CJR3 小时前
redis cluster 部署
java·redis·mybatis