【C++】Unordered_map && Unordered_set

        在C++98中,STL提供了底层为红黑树结构的一系列关联式容器,例如map、set等。它们在搜索数据时效率可达到O(logN),但最糟糕的情况下搜索需要比较红黑树的高度次,若此时树中的节点非常之多,那么搜索效率就非常不理想。

        最理想的搜索是,进行较少的比较次数就能够将元素找到。于是,在C++11中,STL又提供了4个unordered系列的关联式容器,即unordered_map 、unordered_set、unordered_multimap和unordered_multiset。这四个容器与红黑树结构的关联式容器的使用方式基本类似,但不同的是,它们的底层结构为哈希桶。

        本篇博客unordered_map和unordered_set的特性和常见用法,并搭配对STL源码的模拟实现,旨在更好地帮助读者理解容器的功能。

目录

一、无序的关联式容器

1.Unordered_map 

2.Unordered_set

二、同一种底层数据结构支持两种不同的容器

1.底层哈希桶的实现

2.上层的初步封装

· unordered_map

· unordered_set

3.普通迭代器

· 基本结构

 · 相关功能

· 上层的进一步封装

4.const迭代器

· 基本结构

 · 上层的进一步封装

5.insert() 的返回值与operator[ ]

6.键值不匹配的解决方案

7.模拟实现的完整代码

· HashTable.h

· unordered_map.h

· unordered_set.h


一、无序的关联式容器

1.Unordered_map 

        Unordered_map(无序映像)是一种存储<key,value>键值对的关联式容器,使用时需包含该容器的头文件“#include<unordered_map>”,底层为哈希桶,支持通过key快速地索引到与之对应的value(为了能在常数范围内找到key所对应的value,它将具有相同哈希地址的键值对放在相同的桶中)。

        对于unordered_map中的元素,键值key通常用于惟一地标识它,实值value则表示它本身的值。键值key和实值value是一一对应的,它们的类型可以不同。它们在容器内没有对按照任何特定的顺序来排序,所以unordered_map相当于是map的”不排序但去重“版本。

        Unordered_map通过key访问单个元素要比map快(这是因为其底层是哈希桶),但在遍历迭代方面效率通常较低。它支持下标访问符[ ],允许使用key作为参数直接访问value。

        Unordered_map的迭代器一般为正向迭代器。

#include<unordered_map>

void test_unordered_map()
{
    unordered_map<string, string> up;               //调用默认构造定义一个空的容器
    unordered_map<string, string> up2(up);//调用拷贝构造创建并初始化一个容器

    //emplace()构造并插入元素
	up.emplace("1", "apple");
	up.emplace("2", "banana");
	up.emplace("3", "orange");
    up.emplace("4", "pear");
    up.emplace("5", "watermelon");
    //迭代器遍历
    for ( auto it = up.begin(); it != up.end(); ++it )
    {
        cout << " " << it->first << ":" << it->second << endl;
    }
    
    //insert()插入元素
    up2.insert ( {{"sugar",0.8},{"salt",0.1}} );       //默认构造插入
    pair<string,double> myshopping ("baking powder",0.3);
    up2.insert (myshopping);                           //拷贝构造插入                       
    up2.insert (make_pair<string,double>("eggs",6.0)); //移动构造插入
    up2.insert (up.begin(), up.end());                 //迭代器范围插入
    //范围for遍历容器
	for (auto& kv : up2)
	{
		cout << kv.first << ":" << kv.second << endl;
	}

    //通过[]也可以插入元素
    unordered_map<string, string> dict;
	dict["sort"] = "排序";
	dict["insert"] = "插入";
	dict["string"] = "字符串";
	dict["left"];
	for (auto& kv : dict)
	{
		cout << kv.first << ":" << kv.second << endl;
	}

    //查找
    auto it = dict.find(sort); 
    if ( it == dict.end() ) 
    {
        cout << "not found" << endl;
    }
    else 
    {
        cout << it->first << " is " << it->second << endl;
    }
    string s="insert";
    if (dict.count(s)>0)
    {
      cout << "dict has " << s << endl;
    }


    //删除
    up.erase ( up.begin() );             //迭代器指定删除
    up.erase ("2");                      //key指定删除
    up.erase ( up.find("3"), up.end() ); //迭代器范围删除

    //验空+查大小
    if( up.empty() )
    {
        cout << up.size() << endl;
    }
}

2.Unordered_set

        Unordered_set(无序集合)是一种只存储键值key的关联式容器,使用时需包含该容器的头文件“#include<unordered_set>”,底层为哈希桶,支持对键值key的快速检索。

        对于unordered_set中的元素,它们的实值同时也是唯一标识它的键值。键值key在设置上是不可变的,因此,容器中的元素不可修改,只可插入和删除。容器中的元素没有对按照任何特定的顺序来排序,所以unordered_set相当于是set的”不排序但去重“版本。

        Unordered_set访问单个元素要比set快(这是因为其底层是哈希桶),但在遍历迭代方面效率通常较低。

        Unordered_set的迭代器一般为正向迭代器。

void test_unordered_set()
{
	//unordered_set仅能去重

	unordered_set<int> us;     //调用默认构造定义一个空的容器
    unordered_set<int> us2(us);//调用拷贝构造创建并初始化一个容器

    //插入
	us.insert(3);
	us.insert(1);
	us.insert(3);
	us.insert(4);
	us.insert(5);
    //迭代器遍历
	unordered_set<int>::iterator it = us.begin(); 
	while (it != us.end())
	{
		cout << *it << "";//3 1 4 5 
		++it;
	}
	cout << endl;

    //查找
    for (auto& x: us) 
    {
        if (myset.count(x)>0) cout << "us has " << x << endl;
        else cout << "us has no " << x << endl;
    }
    unordered_set<int>::const_iterator got = us.find (6);
    if ( got == myset.end() ) cout << "not found in us";
    else cout << *got << " is in us";

    //删除
    us.erase ( us.begin() );                    
    us.erase (4);                        
    us.erase (us.find(1), us.end() ); 

}

二、同一种底层数据结构支持两种不同的容器

 (这一小节在【C++】map 和 set 中有逻辑相似的、更加详细的STL源码解析)

1.底层哈希桶的实现

(关于哈希桶的更多解析详见:【数据结构】哈希

//HashTable.h

#include<vector>

template<class K>
struct DefaultHashFunc
{
	size_t operator()(const K& key)
	{
		return (size_t)key;
	}
};
template<>
struct DefaultHashFunc<string>
{
	size_t operator()(const string& str)
	{
		size_t hash = 0;
		for (auto ch : str)
		{
			hash *= 131;
			hash += ch;
		}

		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 HashFunc = DefaultHashFunc<K>>
	class HashTable
	{
		typedef HashNode<T> Node;

	public:
		HashTable()
		{
			_table.resize(10, nullptr);
		}

		~HashTable()
		{
			for (size_t i = 0; i < _table.size(); i++)
			{
				Node* cur = _table[i];
				while (cur)
				{
					Node* next = cur->_next;
					delete cur;
					cur = next;
				}
				_table[i] = nullptr;
			}
		}

		bool Insert(const T& data)
		{
			KeyOfT kot;

			if(Find(kot(data)))
			{
				return false;
			}

			HashFunc hf;

			if (_n == _table.size())
			{
				size_t newSize = _table.size()*2;
				vector<Node*> newTable;
				newTable.resize(newSize, nullptr);

				for (size_t i = 0; i < _table.size(); i++)
				{
					Node* cur = _table[i];
					while (cur)
					{
						Node* next = cur->_next;

						size_t hashi = hf(kot(cur->_data)) % newSize;
						cur->_next = newTable[hashi];
						newTable[hashi] = cur;

						cur = next;
					}

					_table[i] = nullptr;
				}

				_table.swap(newTable);
			}

			size_t hashi = hf(kot(data)) % _table.size();
			Node* newnode = new Node(data);
			newnode->_next = _table[hashi];
			_table[hashi] = newnode;
			++_n;
			return true;
		}

		Node* Find(const K& key)
		{
			HashFunc hf;
			KeyOfT kot;
			size_t hashi = hf(key) % _table.size();
			Node* cur = _table[hashi];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					return cur;
				}

				cur = cur->_next;
			}

			return nullptr;
		}

		bool Erase(const K& key)
		{
			HashFunc hf;
			KeyOfT kot;
			size_t hashi = hf(key) % _table.size();
			Node* prev = nullptr;
			Node* cur = _table[hashi];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					if (prev == nullptr)
					{
						_table[hashi] = cur->_next;
					}
					else
					{
						prev->_next = cur->_next;
					}

					--_n;
					delete cur;	
					return true;
				}

				prev = cur;
				cur = cur->_next;
			}

			return false;
		}

	private:
		vector<Node*> _table; // 挂满链表的指针数组
		size_t _n = 0;        // 有效数据个数
	};
}

2.上层的初步封装

· unordered_map

//unordered_map.h

#include"HashTable.h"

namespace CVE
{
	template<class K, class V>
	class unordered_map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
	private:
		hash_bucket::HashTable<K, pair<K, V>, MapKeyOfT> _ht;
	};
}

// unordered_map -> hash_bucket::HashTable<K, pair<K, V>> _ht;
// unordered_set -> hash_bucket::HashTable<K, K> _ht;

· unordered_set

//unordered_set.h

#include"HashTable.h"

namespace CVE
{
	template<class K>
	class unordered_set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	private:
		hash_bucket::HashTable<K, K, SetKeyOfT> _ht;
	};
}

// unordered_map -> hash_bucket::HashTable<K, pair<K, V>> _ht;
// unordered_set -> hash_bucket::HashTable<K, K> _ht;

3.普通迭代器

· 基本结构

//HashTable.h


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 HashFunc>
	class HashTable;

    //普通迭代器
	template<class K, class T, class KeyOfT, class HashFunc>
	struct HTIterator
	{
		typedef HashNode<T> Node;
		typedef HTIterator<K, T, KeyOfT, HashFunc> Self;

		Node* _node;                             //哈希桶的当前节点
		HashTable<K, T, KeyOfT, HashFunc>* _pht; //哈希桶的指针

        //构造函数
		HTIterator(Node* node, HashTable<K, T, KeyOfT, HashFunc>* pht)
			:_node(node)
			,_pht(pht)
		{}

        //...

    //哈希桶(哈希表)
	template<class K, class T, class KeyOfT, class HashFunc = DefaultHashFunc<K>>
	class HashTable
	{
        //...

	public:
		typedef HTIterator<K, T, KeyOfT, HashFunc> iterator;

        //...
	};

}

 · 相关功能

//HashTable.h

namespace hash_bucket
{

    //...

	template<class K, class T, class KeyOfT, class HashFunc>
	struct HTIterator
	{
		typedef HashNode<T> Node;
		typedef HTIterator<K, T, KeyOfT, HashFunc> Self;

		Node* _node;                             //哈希桶的当前节点
		HashTable<K, T, KeyOfT, HashFunc>* _pht; //哈希桶的指针

		HTIterator(Node* node, HashTable<K, T, KeyOfT, HashFunc>* pht)
			:_node(node)
			,_pht(pht)
		{}

        //*重载
		T& operator*()
		{
			return _node->_data; 
		}

        //->重载
		T* operator->()
		{
			return &_node->_data;
		}

        //++重载
		Self& operator++()
		{
            //_node是某一个桶中的当前节点的指针
            // 当前桶中链表还没完,就遍历到下一个链表节点
			if (_node->_next)
			{
				_node = _node->_next;
			}
            // 当前桶中链表走完了,就去找下一个挂了链表的桶(即不为空的桶)
			else
			{
                // 取当前桶的哈希地址
				KeyOfT kot;
				HashFunc hf;
				size_t hashi = hf(kot(_node->_data)) % _pht->_table.size();
				// 去下一个相邻的哈希地址查找下一个不为空的桶
				++hashi;
				while (hashi < _pht->_table.size())
				{
                    //找到了,将_node(当前节点的指针)置为找到的桶的指针,并返回
					if (_pht->_table[hashi])
					{
						_node = _pht->_table[hashi];
						return *this;
					}
                    //没找到就再去下一个相邻的哈希地址找
					else
					{
						++hashi; 
					}
				}

				_node = nullptr;
			}

            //遍历到桶中的下一个节点,或哈希表遍历完了,都返回当前节点(的指针)
			return *this;
		}

        //!=重载
		bool operator!=(const Self& s)
		{
			return _node != s._node;
		}
	};

    template<class K, class T, class KeyOfT, class HashFunc = DefaultHashFunc<K>>
	class HashTable
	{
		typedef HashNode<T> Node;

		// 友元声明
        // 否则HTIterator的对象无法访问HashTable的私有成员
		template<class K, class T, class KeyOfT, class HashFunc>
		friend struct HTIterator;

	public:
		typedef HTIterator<K, T, KeyOfT, HashFunc> iterator;

		iterator begin()
		{
			// 找第一个桶
			for (size_t i = 0; i < _table.size(); i++)
			{
				Node* cur = _table[i];
				if (cur)
				{
					return iterator(cur, this);
				}
			}

			return iterator(nullptr, this);
		}

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

        //...

    }

}

· 上层的进一步封装

  · unordered_map

//unordered_map.h

#include"HashTable.h"

namespace CVE
{
	template<class K, class V>
	class unordered_map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
	public:
		typedef typename hash_bucket::HashTable<K, pair<K, V>, MapKeyOfT>::iterator iterator;

		iterator begin()
		{
			return _ht.begin();
		}

		iterator end()
		{
			return _ht.end();
		}

	private:
		hash_bucket::HashTable<K, pair<K, V>, MapKeyOfT> _ht;
	};
}

 · unordered_set

//unordered_set.h

#include"HashTable.h"

namespace CVE
{
	template<class K>
	class unordered_set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		typedef typename hash_bucket::HashTable<K, K, SetKeyOfT>::iterator iterator;

		iterator begin()
		{
			return _ht.begin();
		}

		iterator end()
		{
			return _ht.end();
		}

	private:
		hash_bucket::HashTable<K, K, SetKeyOfT> _ht;
	};
}

4.const迭代器

· 基本结构

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 HashFunc>
	class HashTable;

	template<class K, class T, class Ptr, class Ref, class KeyOfT, class HashFunc>
	struct HTIterator
	{
		typedef HashNode<T> Node;
		typedef HTIterator<K, T, Ptr, Ref, KeyOfT, HashFunc> Self;
		typedef HTIterator<K, T, T*, T&, KeyOfT, HashFunc> Iterator;

		Node* _node;
		const HashTable<K, T, KeyOfT, HashFunc>* _pht;

		HTIterator(Node* node, const HashTable<K, T, KeyOfT, HashFunc>* pht)
			:_node(node)
			, _pht(pht)
		{}

		// 对于普通迭代器对象,这是拷贝构造
		// 对于const迭代器对象,这是构造,同时也支持普通迭代器去构造const迭代器
		HTIterator(const Iterator& it)
			:_node(it._node)
			, _pht(it._pht)
		{}

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

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

		Self& operator++()
		{
			if (_node->_next)
			{
				_node = _node->_next;
			}
			else
			{
				KeyOfT kot;
				HashFunc hf;
				size_t hashi = hf(kot(_node->_data)) % _pht->_table.size();
				++hashi;
				while (hashi < _pht->_table.size())
				{
					if (_pht->_table[hashi])
					{
						_node = _pht->_table[hashi];
						return *this;
					}
					else
					{
						++hashi;
					}
				}

				_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 HashFunc = DefaultHashFunc<K>>
	class HashTable
	{
		typedef HashNode<T> Node;

		// 友元声明
		template<class K, class T, class Ptr, class Ref, class KeyOfT, class HashFunc>
		friend struct HTIterator;
	public:
		typedef HTIterator<K, T, T*, T&, KeyOfT, HashFunc> iterator;
		typedef HTIterator<K, T, const T*, const T&, KeyOfT, HashFunc> const_iterator;

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

			return iterator(nullptr, this);
		}

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

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

			return const_iterator(nullptr, this);
		}

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

    //...

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

 · 上层的进一步封装

 · unordered_map

#include"HashTable.h"

namespace CVE
{
	template<class K, class V>
	class unordered_map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<const K, V>& kv)
			{
				return kv.first;
			}
		};
	public:
		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();
		}

        //...

	private:
		hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT> _ht;
	};
}

 · unordered_set

#pragma once

#include"HashTable.h"

namespace CVE
{
	template<class K>
	class unordered_set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		typedef typename hash_bucket::HashTable<K, K, SetKeyOfT>::const_iterator iterator;
		typedef typename hash_bucket::HashTable<K, K, SetKeyOfT>::const_iterator const_iterator;

		const_iterator begin() const
		{
			return _ht.begin();
		}

		const_iterator end() const
		{
			return _ht.end();
		}

		//...

	private:
		hash_bucket::HashTable<K, K, SetKeyOfT> _ht;
	};
}

5.insert() 的返回值与operator[ ]

//HashTable.h


namespace hash_bucket
{
 
   //...

    template<class K, class T, class KeyOfT, class HashFunc = DefaultHashFunc<K>>
	class HashTable
	{
		typedef HashNode<T> Node;

		template<class K, class T, class Ptr, class Ref, class KeyOfT, class HashFunc>
		friend struct HTIterator;

	public:
		typedef HTIterator<K, T, T*, T&, KeyOfT, HashFunc> iterator;
		typedef HTIterator<K, T, const T*, const T&, KeyOfT, HashFunc> const_iterator;

        //...

        pair<iterator, bool> Insert(const T& data)
		{
			KeyOfT kot;

			iterator it = Find(kot(data));
			if(it != end())
			{
				return make_pair(it, false);
			}

			HashFunc hf;

			if (_n == _table.size())
			{
				//size_t newSize = _table.size() * 2;
				size_t newSize = GetNextPrime(_table.size());
				vector<Node*> newTable;
				newTable.resize(newSize, nullptr);

				for (size_t i = 0; i < _table.size(); i++)
				{
					Node* cur = _table[i];
					while (cur)
					{
						Node* next = cur->_next;

						size_t hashi = hf(kot(cur->_data)) % newSize;
						cur->_next = newTable[hashi];
						newTable[hashi] = cur;

						cur = next;
					}

					_table[i] = nullptr;
				}

				_table.swap(newTable);
			}

			size_t hashi = hf(kot(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)
		{
			HashFunc hf;
			KeyOfT kot;
			size_t hashi = hf(key) % _table.size();
			Node* cur = _table[hashi];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					return iterator(cur, this);
				}

				cur = cur->_next;
			}

			return end();
		}

        //...

	private:
		vector<Node*> _table; 
		size_t _n = 0; 
	};
}
//unordered_map.h


#include"HashTable.h"

namespace CVE
{
	template<class K, class V>
	class unordered_map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<const K, V>& kv)
			{
				return kv.first;
			}
		};
	public:
		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();
		}

		pair<iterator, bool> insert(const pair<K, V>& kv)
		{
			return _ht.Insert(kv);
		}

		V& operator[](const K& key)
		{
			pair<iterator, bool> ret = _ht.Insert(make_pair(key, V()));
			return ret.first->second;
		}
	private:
		hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT> _ht;
	};
}

6.键值不匹配的解决方案

//unordered_set.h


#include"HashTable.h"

namespace CVE
{
	template<class K>
	class unordered_set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		typedef typename hash_bucket::HashTable<K, K, SetKeyOfT>::const_iterator iterator;
		typedef typename hash_bucket::HashTable<K, K, SetKeyOfT>::const_iterator const_iterator;


		const_iterator begin() const
		{
			return _ht.begin();
		}

		const_iterator end() const
		{
			return _ht.end();
		}

		pair<const_iterator, bool> insert(const K& key)
		{
			pair<typename hash_bucket::HashTable<K, K, SetKeyOfT>::iterator, bool> ret = _ht.Insert(key);
           //先用一个<普通迭代器,布尔值>的对象接收插入方法的返回值
			return pair<const_iterator, bool>(ret.first, ret.second);
            //再将对象中的普通迭代器通过构造函数构造为const迭代器

		}
	private:
		hash_bucket::HashTable<K, K, SetKeyOfT> _ht;
	};
}

7.模拟实现的完整代码

· HashTable.h

#include<vector>

template<class K>
struct DefaultHashFunc
{
	size_t operator()(const K& key)
	{
		return (size_t)key;
	}
};
template<>
struct DefaultHashFunc<string>
{
	size_t operator()(const string& str)
	{
		size_t hash = 0;
		for (auto ch : str)
		{
			hash *= 131;
			hash += ch;
		}

		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 HashFunc>
	class HashTable;

	template<class K, class T, class Ptr, class Ref, class KeyOfT, class HashFunc>
	struct HTIterator
	{
		typedef HashNode<T> Node;
		typedef HTIterator<K, T, Ptr, Ref, KeyOfT, HashFunc> Self;
		typedef HTIterator<K, T, T*, T&, KeyOfT, HashFunc> Iterator;

		Node* _node;
		const HashTable<K, T, KeyOfT, HashFunc>* _pht;

		HTIterator(Node* node, const HashTable<K, T, KeyOfT, HashFunc>* pht)
			:_node(node)
			, _pht(pht)
		{}

		HTIterator(const Iterator& it)
			:_node(it._node)
			, _pht(it._pht)
		{}

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

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

		Self& operator++()
		{
			if (_node->_next)
			{
				_node = _node->_next;
			}
			else
			{
				KeyOfT kot;
				HashFunc hf;
				size_t hashi = hf(kot(_node->_data)) % _pht->_table.size();
				++hashi;
				while (hashi < _pht->_table.size())
				{
					if (_pht->_table[hashi])
					{
						_node = _pht->_table[hashi];
						return *this;
					}
					else
					{
						++hashi;
					}
				}

				_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 HashFunc = DefaultHashFunc<K>>
	class HashTable
	{
		typedef HashNode<T> Node;

		template<class K, class T, class Ptr, class Ref, class KeyOfT, class HashFunc>
		friend struct HTIterator;
	public:
		typedef HTIterator<K, T, T*, T&, KeyOfT, HashFunc> iterator;
		typedef HTIterator<K, T, const T*, const T&, KeyOfT, HashFunc> const_iterator;

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

			return iterator(nullptr, this);
		}

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

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

			return const_iterator(nullptr, this);
		}

		const_iterator end() const
		{
			return const_iterator(nullptr, this);
		}
        
        //每次快速取模的是一个类似两倍关系的素数,可以更好地支持除留余数法寻址和扩容
        //但这个方法并没有明确的理论依据
        //C++的STL库中用了这样的方法,而Java库中没有用
		size_t GetNextPrime(size_t prime)
		{
			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 < PRIMECOUNT; ++i)
			{
				if (primeList[i] > prime)
					return primeList[i];
			}

			return primeList[i];
		}

		HashTable()
		{
			_table.resize(GetNextPrime(1), nullptr);
		}

		~HashTable()
		{
			for (size_t i = 0; i < _table.size(); i++)
			{
				Node* cur = _table[i];
				while (cur)
				{
					Node* next = cur->_next;
					delete cur;
					cur = next;
				}

				_table[i] = nullptr;
			}
		}

		pair<iterator, bool> Insert(const T& data)
		{
			KeyOfT kot;

			iterator it = Find(kot(data));
			if(it != end())
			{
				return make_pair(it, false);
			}

			HashFunc hf;

			if (_n == _table.size())
			{
				size_t newSize = GetNextPrime(_table.size());
				vector<Node*> newTable;
				newTable.resize(newSize, nullptr);

				for (size_t i = 0; i < _table.size(); i++)
				{
					Node* cur = _table[i];
					while (cur)
					{
						Node* next = cur->_next;

						size_t hashi = hf(kot(cur->_data)) % newSize;
						cur->_next = newTable[hashi];
						newTable[hashi] = cur;

						cur = next;
					}

					_table[i] = nullptr;
				}

				_table.swap(newTable);
			}

			size_t hashi = hf(kot(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)
		{
			HashFunc hf;
			KeyOfT kot;
			size_t hashi = hf(key) % _table.size();
			Node* cur = _table[hashi];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					return iterator(cur, this);
				}

				cur = cur->_next;
			}

			return end();
		}

		bool Erase(const K& key)
		{
			HashFunc hf;
			KeyOfT kot;
			size_t hashi = hf(key) % _table.size();
			Node* prev = nullptr;
			Node* cur = _table[hashi];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					if (prev == nullptr)
					{
						_table[hashi] = cur->_next;
					}
					else
					{
						prev->_next = cur->_next;
					}

					--_n;
					delete cur;	
					return true;
				}

				prev = cur;
				cur = cur->_next;
			}


			return false;
		}

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

· unordered_map.h


#include"HashTable.h"

namespace CVE
{
	template<class K, class V>
	class unordered_map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<const K, V>& kv)
			{
				return kv.first;
			}
		};
	public:
		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();
		}

		pair<iterator, bool> insert(const pair<K, V>& kv)
		{
			return _ht.Insert(kv);
		}

		V& operator[](const K& key)
		{
			pair<iterator, bool> ret = _ht.Insert(make_pair(key, V()));
			return ret.first->second;
		}
	private:
		hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT> _ht;
	};
}

· unordered_set.h

#include"HashTable.h"

namespace CVE
{
	template<class K>
	class unordered_set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		typedef typename hash_bucket::HashTable<K, K, SetKeyOfT>::const_iterator iterator;
		typedef typename hash_bucket::HashTable<K, K, SetKeyOfT>::const_iterator const_iterator;


		const_iterator begin() const
		{
			return _ht.begin();
		}

		const_iterator end() const
		{
			return _ht.end();
		}

		pair<const_iterator, bool> insert(const K& key)
		{
			pair<typename hash_bucket::HashTable<K, K, SetKeyOfT>::iterator, bool> ret = _ht.Insert(key);
			return pair<const_iterator, bool>(ret.first, ret.second);
		}
	private:
		hash_bucket::HashTable<K, K, SetKeyOfT> _ht;
	};
}

  • 23
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值