1.源码及框架分析
SGI-STL 3.0 版本的哈希容器历史
hash_map和hash_set的实现结构框架核⼼部分截取出来如下:
第一层:__hashtable_node(节点结构)
cpptemplate <class Value> struct __hashtable_node { __hashtable_node* next; // 单链表指针 Value val; // 存储的实际数据 };第二层:hashtable(核心实现)
cpptemplate <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 提取器模式
- hash_set 使用 identity<Value>,Value 本身就是 Key;调用 get_key(obj) 返回 obj 本身
- 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; // 判断键相等
查找流程:
hash(key)定位桶遍历链表,用
equals(key, node->key)精确匹配第三层:hash_set(set 语义适配器)
cpptemplate <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); } // ... };设计优势:
迭代器为 const:确保 set 中的元素不可修改
insert_unique:set 不允许重复键
完全委托 :
hash_set本身几乎没有代码,所有功能都转发给rep第四层:hash_map(map 语义适配器)
cpptemplate <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 的关键差异:
value_type 不同 :
pair<const Key, T>,键不可修改但值可以迭代器非 const :允许通过迭代器修改
mapped_type提供 operator[]:方便通过键访问值
select1st 提取器:从 pair 中提取键
2.模拟实现unordered_set/map
2.1实现出复⽤哈希表的框架,并⽀持insert
- 参考源码的大框架,unordered_set/map使用我们之前的哈希桶,源码第一个模板参数为存储数据类型二第二个模板参数为键,但为了符合直观理解,我们这里进行调整,key参数就是用K,value参数就用V
- 其次跟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++核心实现: 下面仿照源码实现迭代器++操作。
cpptemplate <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; }
- 哈希表的迭代器是单向迭代器,不支持--操作。
- 这⾥的难点是operator++的实现。iterator中有⼀个指向结点的指针,如果当前桶下⾯还有结点,则结点的指针指向下⼀个结点即可。如果当前桶⾛完了,则需要想办法计算找到下⼀个桶。这⾥的难点是反⽽是结构设计的问题,参考上⾯的源码,我们可以看到iterator中除了有结点的指针,还有哈希表对象的指针,这样当前桶⾛完了,要计算下⼀个桶就相对容易多了,⽤key值计算出当前桶位置,依次往后找下⼀个不为空的桶即可。
- begin()返回第⼀个桶中第⼀个节点指针构造的迭代器,这⾥end()返回迭代器可以⽤空表⽰。
- 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;
代码具体实现:
**注意事项1:**HashTable::Begin()返回第一个桶不为空的节点时,可以先加一个_n==0的判断,防止遍历空的哈希表是造成不必要的性能浪费
**注意事项2:**前后依赖的问题,当我们要使用类型/函数/变量时便需在前面找到定义(除了类域,类里面当作一个整体),于是HTIterator和HashTable是相互依赖的,用顺序是解决不了问题的,这里可以使用前置声明的方法来解决,而且在使用类模板的前置声明中模板缺省参数不能加,只需要告诉编译器要哪一些类型。这是 C++ 模板前置声明的一个重要规则:声明中只写参数,不写缺省值
cpptemplate<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成员无法在类外面直接访问,解决方法:
- 可以直接传vector这个数组的地址过来(其实事项2也可以这样解决,但是破坏了封装外部代码也能访问桶数组,不安全,还有各种桶信息的缺失不推荐)
- 使用友元(STL标准做法)但注意,类模板进行友元声明(或者前置声明)时要将模板参数带过来
cpptemplate<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有权限放大的问题,从而不能正常构造迭代器
cpptemplate<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的[]支持
- unordered_map要⽀持[]主要需要修改insert返回值⽀持,修改HashTable中的insert返回值为 pair<Iterator, bool> Insert(const T& data)
- 有了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; }

