STL 源码解密:unordered 系列容器的底层复用与哈希策略

1.源码及框架分析

SGI-STL 3.0 版本的哈希容器历史

hash_map和hash_set的实现结构框架核⼼部分截取出来如下:


第一层:__hashtable_node(节点结构)

cpp 复制代码
template <class Value>
struct __hashtable_node
{
    __hashtable_node* next;  // 单链表指针
    Value val;               // 存储的实际数据
};

第二层:hashtable(核心实现)

cpp 复制代码
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;             // 从 Value 中提取 Key 的函数
    typedef __hashtable_node<Value> node;
    vector<node*, Alloc> buckets;   // 桶数组(指针数组)
    size_type num_elements;         // 元素总数
    
public:
    typedef __hashtable_iterator<...> iterator;
    
    pair<iterator, bool> insert_unique(const value_type& obj);
    const_iterator find(const key_type& key) const;
    // ... 其他接口
};
  • 关键设计1:ExtractKey 提取器模式
  1. hash_set 使用 identity<Value>,Value 本身就是 Key;调用 get_key(obj) 返回 obj 本身
  2. hash_map 使用 select1st,从 pair<const Key, T> 中提取 .first;调用 get_key(obj) 返回 obj.first
  • 关键设计2:buckets 的类型

与之前我们模拟实现的哈希桶原理一样使用指针数组,vector<node*, Alloc> buckets; 存储的是指针,不是 node 对象

  • 关键设计3:分离的哈希函数和相等比较

hasher hash; // 计算哈希值
key_equal equals; // 判断键相等

  • 查找流程:

    1. hash(key) 定位桶

    2. 遍历链表,用 equals(key, node->key) 精确匹配

第三层:hash_set(set 语义适配器)

cpp 复制代码
template <class Value, class HashFcn = hash<Value>,
          class EqualKey = equal_to<Value>, class Alloc = alloc>
class hash_set {
private:
    // 关键:hashtable 的模板参数实例化
    typedef hashtable<Value, Value, HashFcn, 
                      identity<Value>,  // 提取器:返回自身
                      EqualKey, Alloc> ht;
    ht rep;  // 组合一个哈希表对象
    
public:
    // 迭代器是 const_iterator,禁止修改键值
    typedef typename ht::const_iterator iterator;
    typedef typename ht::const_iterator const_iterator;
    
    // 所有接口直接转发给 rep
    iterator begin() const { return rep.begin(); }
    pair<iterator, bool> insert(const value_type& obj) {
        return rep.insert_unique(obj);  // 不允许重复
    }
    iterator find(const key_type& key) const { return rep.find(key); }
    // ...
};

设计优势:

  1. 迭代器为 const:确保 set 中的元素不可修改

  2. insert_unique:set 不允许重复键

  3. 完全委托hash_set 本身几乎没有代码,所有功能都转发给 rep

第四层:hash_map(map 语义适配器)

cpp 复制代码
template <class Key, class T, class HashFcn = hash<Key>,
          class EqualKey = equal_to<Key>, class Alloc = alloc>
class hash_map {
private:
    // 关键差异:value_type 是 pair<const Key, T>
    typedef hashtable<pair<const Key, T>, Key, HashFcn,
                      select1st<pair<const Key, T> >,  // 提取 .first
                      EqualKey, Alloc> ht;
    ht rep;
    
public:
    typedef typename ht::iterator iterator;  // 普通迭代器(可修改 mapped_type)
    
    // operator[] 的巧妙实现
    T& operator[](const key_type& key) {
        return rep.find_or_insert(key).second;
    }
};

与 hash_set 的关键差异

  1. value_type 不同pair<const Key, T>,键不可修改但值可以

  2. 迭代器非 const :允许通过迭代器修改 mapped_type

  3. 提供 operator[]:方便通过键访问值

  4. select1st 提取器:从 pair 中提取键

2.模拟实现unordered_set/map

2.1实现出复⽤哈希表的框架,并⽀持insert

  1. 参考源码的大框架,unordered_set/map使用我们之前的哈希桶,源码第一个模板参数为存储数据类型二第二个模板参数为键,但为了符合直观理解,我们这里进行调整,key参数就是用K,value参数就用V
  2. 其次跟map和set相⽐⽽⾔unordered_map和unordered_set的模拟实现类结构更复杂⼀点,但是⼤框架和思路是完全类似的。因为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⽐较相等,具体细节参考如下代码实现。

注意:#pragma once是给头文件自己用的,用来防止自己(也就是会被重复包含的头文件)被重复包含

2.2unordered系列的iterator支持

在STL中,unordered_map/set的底层是基于哈希表实现的。与vector等容器不同,哈希表的内存布局是不连续的,因此不能简单地使用原生指针作为迭代器。我们需要封装节点指针和桶信息,并通过重载运算符来实现迭代器的行为。这里仿照C++list全解析-CSDN博客list迭代器。


operator++核心实现: 下面仿照源码实现迭代器++操作。

cpp 复制代码
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;        // 1 保存当前节点
	cur = cur->next;              // 2 尝试移动到链表的下一个节点

	if (!cur) {                   // 3 当前桶已遍历完
		// 关键:根据节点值重新计算它所在的桶位置
		size_type bucket = ht->bkt_num(old->val);

		// 4 向后查找下一个非空桶
		while (!cur && ++bucket < ht->buckets.size())
			cur = ht->buckets[bucket];
	}
	return *this;
}
  1. 哈希表的迭代器是单向迭代器,不支持--操作。
  2. 这⾥的难点是operator++的实现。iterator中有⼀个指向结点的指针,如果当前桶下⾯还有结点,则结点的指针指向下⼀个结点即可。如果当前桶⾛完了,则需要想办法计算找到下⼀个桶。这⾥的难点是反⽽是结构设计的问题,参考上⾯的源码,我们可以看到iterator中除了有结点的指针,还有哈希表对象的指针,这样当前桶⾛完了,要计算下⼀个桶就相对容易多了,⽤key值计算出当前桶位置,依次往后找下⼀个不为空的桶即可。
  3. begin()返回第⼀个桶中第⼀个节点指针构造的迭代器,这⾥end()返回迭代器可以⽤空表⽰。
  4. unordered_set的iterator也不⽀持修改,我们把unordered_set的第⼆个模板参数改成const K即可, HashTable<K, const K, SetKeyOfT, Hash> _ht;
  5. unordered_map的iterator不⽀持修改key但是可以修改value,我们把unordered_map的第⼆个模板参数pair的第⼀个参数改成const K即可, HashTable<K, pair<const K, V>, MapKeyOfT, Hash> _ht;

代码具体实现:

**注意事项1:**HashTable::Begin()返回第一个桶不为空的节点时,可以先加一个_n==0的判断,防止遍历空的哈希表是造成不必要的性能浪费

**注意事项2:**前后依赖的问题,当我们要使用类型/函数/变量时便需在前面找到定义(除了类域,类里面当作一个整体),于是HTIterator和HashTable是相互依赖的,用顺序是解决不了问题的,这里可以使用前置声明的方法来解决,而且在使用类模板的前置声明中模板缺省参数不能加,只需要告诉编译器要哪一些类型。这是 C++ 模板前置声明的一个重要规则:声明中只写参数,不写缺省值

cpp 复制代码
template<class K,class T,class Ptr ,class Ref,class KeyOfT,class Hash>
struct HTIterator
{
	//...
private:
	Node* _node;
	const HashTable<K, T, KeyOfT, Hash>* _pht;
};

template<class K, class T, class keyofT, class Hash>
class HashTable
{
public:
	typedef HashNode<T> Node;

	typedef HTIterator<K, T, T*, T&, keyofT,Hash> Iterator;
	typedef HTIterator<K, T, const T*, const T&, keyofT,  Hash> Const_Iterator;
    //...
}

**注意事项3:**我们要通过_pht获取哈希表的大小,以便在需要换下一个桶遍历时方便计算当前节点哈希值时,由于_tables为HashTable的private成员无法在类外面直接访问,解决方法:

  1. 可以直接传vector这个数组的地址过来(其实事项2也可以这样解决,但是破坏了封装外部代码也能访问桶数组,不安全,还有各种桶信息的缺失不推荐)
  2. 使用友元(STL标准做法)但注意,类模板进行友元声明(或者前置声明)时要将模板参数带过来
cpp 复制代码
template<class K, class T, class keyofT, class Hash>
class HashTable
{
	//friend struct HTIterator;类模板友元要带上模板参数
	template<class K, class T, class Ptr, class Ref, class KeyOfT, class Hash>
	friend struct HTIterator;
    //...
}

注意事项4:当我们在实现const迭代器时this有权限放大的问题,从而不能正常构造迭代器

cpp 复制代码
template<class K,class T,class Ptr ,class Ref,class KeyOfT,class Hash>
struct HTIterator
{
	typedef HashNode<T> Node;
	typedef HTIterator<K, T, Ptr,Ref, KeyOfT, Hash> Self;

	HTIterator(Node* node, const HashTable<K, T, KeyOfT, Hash>*pht)
		:_node(node)    //  ^添加const修饰
		,_pht(pht)
	{}
    //...
}

//HashTable::
Const_Iterator Begin()const
{
	if (_n == 0)
		return End();
	for (size_t i = 0; i < _tables.size(); ++i)
	{
		if (_tables[i])
			return Const_Iterator(_tables[i], this);//const 
	}
	return End();
}
//this 类型const HashTable *const this
Const_Iterator End()const
{
	return Const_Iterator(nullptr, this);
}

这个this的类型是const HashTable *const this 所以不能直接传给HashTable*pht,权限不能放大,所以要添加const修饰,const迭代器是const HashTable *const this权限平移,普通迭代器是HashTable *const this权限缩小

2.3unordered_map的[]支持

  1. unordered_map要⽀持[]主要需要修改insert返回值⽀持,修改HashTable中的insert返回值为 pair<Iterator, bool> Insert(const T& data)
  2. 有了insert⽀持[]实现就很简单了,具体参考下⾯代码实现

3.unordered_set/map模拟实现完整代码

hashbacket.h

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<iostream>

using namespace std;

#include<unordered_set>
#include<unordered_map>
#include<map>
#include<set>
#include<vector>
#include<forward_list>



namespace hash_backet
{
	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);//[first, last]
		return pos == last ? *(last - 1) : *pos;
	}


	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 Ptr ,class Ref,class KeyOfT,class Hash>
	struct HTIterator
	{
		typedef HashNode<T> Node;
		typedef HTIterator<K, T, Ptr,Ref, KeyOfT, Hash> Self;

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

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

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

		Self& operator++()
		{//空迭代器检查(end() 迭代器)
			if (_node == nullptr)
				return *this;
			KeyOfT kot;
			Hash hash;
			if (_node->_next)//当前桶还有元素,继续往下面走
			{
				_node = _node->_next;
			}
			else//_node->_next==nullptr说明当前桶遍历完了,继续找不为空的桶
			{
				//先取data的key再将key转成哈希值
				size_t hashi = hash(kot(_node->_data)) % _pht->_tables.size();
				hashi++;//从下一个位置开始找
				while (hashi<_pht->_tables.size())
				{
					if (_pht->_tables[hashi])
					{
						_node = _pht->_tables[hashi];
						break;
					}
					hashi++;
				}
				//这里有两种情况,1.所有桶都遍历完了到末尾了2.找到不为空的桶break出来的
				if (hashi==_pht->_tables.size())
				{
					_node = nullptr;//所有桶走完了end()给的空,标识_node
				}
			}
			return *this;
		}

		bool operator==(const Self &s) const
		{
			return _node == s._node;
		}
		//比较节点指针
		bool operator!=(const Self& s)const
		{
			return _node != s._node;
		}
	private:

		Node* _node;
		const HashTable<K, T, KeyOfT, Hash>* _pht;//跨桶遍历需要访问哈希表信息
	};

	
	template<class K, class T, class keyofT, class Hash>
	class HashTable
	{
		//friend struct HTIterator;类模板友元要带上模板参数
		template<class K, class T, class Ptr, class Ref, class KeyOfT, class Hash>
		friend struct HTIterator;

	public:
		typedef HashNode<T> Node;

		typedef HTIterator<K, T, T*, T&, keyofT,Hash> Iterator;
		typedef HTIterator<K, T, const T*, const T&, keyofT,  Hash> Const_Iterator;

		Iterator Begin()
		{
			if (_n==0)//没有元素也就是每个桶是空,直接返回,不用找
			{
				return End();
			}
			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
		{
			if (_n == 0)//没有元素也就是每个桶是空,直接返回,不用找
			{
				return End();
			}
			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);
		}

		HashTable()
			:_n(0)
			, _tables(__stl_next_prime(0))
		{
		}
		~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;
			}
		}

		HashTable(const HashTable& other)
			: _tables(other._tables.size())
			, _n(0) 
		{
			// 遍历other的所有节点,深拷贝到当前表
			for (size_t i = 0; i < other._tables.size(); i++)
			{
				Node* cur = other._tables[i];
				while (cur)
				{
					// 逐个插入节点(深拷贝)
					Insert(cur->_data);
					cur = cur->_next;
				}
			}
		}

		//// 赋值运算符重载(深拷贝)
		//HashTable& operator=(HashTable other)  
		//{
		//	this->_n = other._n;
		//	this->_tables.swap(other._tables);
		//	
		//	return *this;
		//}
		HashTable& operator=(HashTable other)
		{
			swap(other);  // 统一交换
			return *this;
		}

		void swap(HashTable& other) noexcept
		{
			std::swap(_tables, other._tables);
			std::swap(_n, other._n);
		}

		pair<Iterator,bool> Insert(const T& data)
		{
			keyofT kot;
			Hash hash;
			Iterator it = Find(kot(data));
			if (it!=End())//找到了,插入失败
			{
				return {it,false};
			}

			//负载因子等于1时进行扩容
			if (_n==_tables.size())
			{
				vector<Node*> newTables(__stl_next_prime(_tables.size() + 1));
				for (size_t i = 0; i < _tables.size(); i++)
				{
					Node* cur = _tables[i];
					while (cur)
					{
						Node* next = cur->_next;
						//新表头插
						size_t hashi = hash(kot(cur->_data)) % newTables.size();

						cur->_next = newTables[hashi];
						newTables[hashi] = cur;

						cur = next;
					}
					_tables[i] = nullptr;//旧表置空原因如下:
					//语义正确性:移动后,旧表应该表示"空",而不是"指向已移动的节点"
					//防止误用:避免后续代码错误地访问这些指针
					//所有权明确:确保节点只有一个所有者(新表)
					//调试友好:nullptr 明确表示空,便于调试和断言
				}
				_tables.swap(newTables);
			}

			size_t  hashi = hash(kot(data)) % _tables.size();
			Node* newnode = new Node(data);

			newnode->_next = _tables[hashi];
			_tables[hashi] = newnode;
			++_n;
			return {Iterator(newnode,this),true};
		}

		Iterator Find(const K& key)
		{
			keyofT kot;
			Hash hash;
			size_t hashi = hash(key) % _tables.size();
			Node* cur = _tables[hashi];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					return Iterator(cur,this);
				}
				cur = cur->_next;
			}
			return End();
		}

		bool Erase(const K& key)
		{
			keyofT kot;
			Hash hash;
			size_t hashi = hash(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;
					}
					delete cur;
					--_n;
					return true;
				}
				else
				{
					prev = cur;
					cur = cur->_next;
				}
			}
			return false;
		}
	private:
		vector<Node*> _tables;//指针数组
		size_t _n = 0;
	};

}

My_unorderedset.h

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1


#include "hashbacket.h"

namespace MySTL
{
	template<class K,class Hash = HashFunc<K>>
	class unordered_set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)const
			{
				return key;
			}
		};
	public:
		//取类型加typename
		typedef typename hash_backet::HashTable<K, const K, SetKeyOfT, Hash>::Iterator iterator;
		typedef typename hash_backet::HashTable<K,const K,SetKeyOfT, Hash>::Const_Iterator const_iterator;

		iterator begin()
		{
			return _table.Begin();
		}

		iterator end()
		{
			return _table.End();
		}

		const_iterator begin()const
		{
			return _table.Begin();
		}

		const_iterator end()const
		{
			return _table.End();
		}

		pair<iterator,bool> insert(const K&key)
		{
			return _table.Insert(key);
		}

		bool erase(const K& key)
		{
			return _table.Erase(key);
		}
		iterator find(const K& key)
		{
			return _table.Find(key);
		}


	private:
		hash_backet::HashTable<K, const K, SetKeyOfT,Hash> _table;
	};
}

My_unorderedmap.h

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1


#include "hashbacket.h"



namespace MySTL
{
	template<class K>
	struct HashFunc
	{
		size_t operator()(const K& key)const
		{
			return (size_t)key;
		}
	};
	template<>
	struct HashFunc<string>
	{
		size_t operator()(const string& s)const
		{
			size_t hashnum = 0;
			for (auto ch : s)
			{
				hashnum += ch;
				hashnum *= 131;
			}
			return hashnum;
		}
	};


	template<class K,class V,class Hash=HashFunc<K>>
	class unordered_map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)const
			{
				return kv.first;
			}
		};

	public:

		//取类型加typename
		typedef typename hash_backet::HashTable<K, pair<const K,V>, MapKeyOfT,Hash>::Iterator iterator;
		typedef typename hash_backet::HashTable<K, pair<const K, V>, MapKeyOfT, Hash>::Const_Iterator const_iterator;

		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 = insert({ key,V() });
			return ret.first->second;
		}

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

		bool erase(const K&key)
		{
			return _table.Erase(key);
		}
		iterator find(const K& key)
		{
			return  _table.Find(key);
		}
	private:

		hash_backet::HashTable<K,pair<const K,V>, MapKeyOfT,Hash> _table;
	};
}

test.cpp

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1


#include"My_unorderedset.h"
#include"My_unorderedmap.h"

//源码如下
// 
// 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;
//};


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

template<class Con>
void printcontainerset(const Con& con)
{
	for(auto& e:con)
	{
		//e += 10;
		cout << e << " ";
	}
}
void testset1()
{
	MySTL::unordered_set<int> s;
	s.insert(23);
	s.insert(323);
	s.insert(283);
	s.insert(2389);
	s.insert(41);
	s.insert(74802);
	s.insert(74802);
	MySTL::unordered_set<int>::iterator it = s.begin();
	while (it != s.end())
	{
		//*it = 1;
		cout << *it <<" ";
		++it;
	}
	cout << endl;
	for (auto& e : s)
	{
		cout << e << " ";
	}
	cout << endl;
	cout << "printconst" << endl;
	printcontainerset<MySTL::unordered_set<int>>(s);
}

template<class Con>
void printcontainermap(const Con& con)
{
	for (auto& e : con)
	{
		cout << e.first << ":" << e.second << endl;
	}
}
void testmap1()
{
	MySTL::unordered_map<int, int> m;
	m.insert({ 1,1 });
	m.insert({ 1000,2 });
	m.insert({ 38933,3 });
	m.insert({ 4323,4 });
	m.insert({ 532327,5 });
	m.insert({ 6329,6 });
	MySTL::unordered_map<int,int>::iterator it = m.begin();
	while (it != m.end())
	{
		//it->first = 1;修改key err
		//it->second = 2;//修改value ok
		cout << it->first << ":" << it->second << endl;
		++it;
	}
	cout << endl;

	for (auto& e : m)
	{
		cout << e.first << ":" << e.second << endl;
	}
	cout << endl;

	MySTL::unordered_map<string, string> dict;
	dict.insert({ "sort", "排序" });
	dict.insert({ "left", "左边" });
	dict.insert({ "right", "右边" });
}

void testmap2()
{
	MySTL::unordered_map<string, string> dict;
	dict.insert({ "sort", "排序" });
	dict.insert({ "left", "左边" });
	dict.insert({ "right", "右边" });

	MySTL::unordered_map<string, string>::iterator it = dict.begin();
	while (it != dict.end())
	{
		cout << it->first << ":" << it->second << endl;
		++it;
	}
	cout << endl;
	for (auto& kv : dict)
	{
		cout << kv.first << ":" << kv.second << endl;
	}
	cout << endl;
	cout << endl;

	cout << "printconst" << endl;
	printcontainermap<MySTL::unordered_map<string, string>>(dict);
}

void testmap3()
{
	MySTL::unordered_map<string, string> dict;
	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;
}
int main()
{
	testset1();
	testmap1();
	testmap2();
	testmap3();

	return 0;
}
相关推荐
6Hzlia2 小时前
【Hot 100 刷题计划】 LeetCode 215. 数组中的第K个最大元素 | C++ 快速选择与堆排序题解
c++·算法·leetcode
小白菜又菜2 小时前
Leetcode 3070. Count Submatrices with Top-Left Element and Sum Less Than k
算法·leetcode·职场和发展
笨笨饿2 小时前
32_复变函数在工程中实际应用区别于联系
linux·服务器·c语言·人工智能·单片机·算法·学习方法
会编程的土豆2 小时前
【数据结构与算法】拓扑排序2
数据结构·算法·leetcode
Boop_wu2 小时前
[Java 算法] 栈
java·开发语言·算法
追风落叶乔木生2 小时前
字节跳动后端一面全解析|基础+算法真题(2026最新版)
算法·哈希算法
来自远方的老作者2 小时前
第7章 运算符-7.5 比较运算符
开发语言·数据结构·python·算法·代码规范·比较运算符
We་ct2 小时前
LeetCode 201. 数字范围按位与:位运算高效解题指南
开发语言·前端·javascript·算法·leetcode·typescript
wanderist.2 小时前
图论模板整理
算法·深度优先·图论