• C++STL详解(七)哈希封装模拟实现unordered_set&unordered_map


    前言

    在模拟实现unordered_set&unordered_map,我们还需要对之前的哈希表进行一定的改造。

    1.unordered_set 模拟实现

    哈希表模板参数控制

    我们知道 unordered_set 是 K 模型的容器,而 unordered_map 是 KV 模型的容器。
    而我们希望的是用一份哈希表代码就能同时封装出K模型和KV模型,因此哈希表接受的参数就得需要根据情况变化。
    如果上层使用的是unordered_set容器,那么传入哈希表的模板参数就是key和key。

    template<class K>
    class unordered_set
    {
    public:
    	//...
    private:
    	HashBucket<K, K> _hb; //传入底层哈希桶的是K和K
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    但如果上层使用的是unordered_map容器,那么传入哈希表的模板参数就是key以及key和value构成的键值对。

    template<class K, class V>
    class unordered_map
    {
    public:
    	//...
    private:
    	HashTable<K, pair<K, V>> _ht; //传入底层哈希桶的是K以及K和V构成的键值对
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    因此,哈希表接受的模板的参数是T,T可以是K,也可以是pair
    对于哈希表来说,不知道T具体是是吗,因此也就需要一个仿函数KeyOfT来获取T类型中的键值

    template<class K, class V>
    class unordered_map
    {
    	//仿函数
    	struct MapKeyOfT
    	{
    		const K& operator()(const pair<K, V>& kv) //返回键值对当中的键值key
    		{
    			return kv.first;
    		}
    	};
    public:
    	//...
    private:
    	HashTable<K, pair<K, V>, MapKeyOfT> _ht;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    template<class K>
    class unordered_set
    {
    	//仿函数
    	struct SetKeyOfT
    	{
    		const K& operator()(const K& key) //返回键值key
    		{
    			return key;
    		}
    	};
    public:
    	//...
    private:
    	HashTable<K, K, SetKeyOfT> _ht;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    此时哈希模板参数变成了

    template<class K, class T, class KeyOfT>
    class HashBucket
    {
    	// ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    此外,哈希最重要的映射问题我们还没有解决,由于我们采用的是取模求余法来解决哈希冲突的。
    这就需要获取将数组转化为对应的整数,而对哈希本身来说,接收到任何数据都是有可能的,因此哈希函数就不能固定,也得由参数来决定。

    template<class K, class T, class KeyOfT, class HashFunc>
    	class HashTable
    	{
    		//...
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述

    默认成员函数

    哈希表中有两个成员变量,当我们实例化一个对象时:

    • _tables会自动调用vector的默认构造函数完成初始化。
    • _n会根据我们所给的缺省值被设置为0。
    vector<Node*> _tables; //哈希表
    size_t _n = 0; //哈希表中的有效元素个数
    
    • 1
    • 2

    因此就不需要再写构造函数,使用默认生成的构造函数就足够了,但是由于我们后面需要写拷贝构造函数,写了拷贝构造函数后,默认的构造函数就不会生成了,而此时由于不存在默认构造函数,编译会通不过。因此,我们要么就写一个空的默认构造,或者default关键字显示指定生成默认构造函数。

    //构造函数
    HashTable() = default; //显示指定生成默认构造函数
    
    • 1
    • 2

    拷贝构造函数:

    由于我们实现的其实是哈希桶,也就是vector中是一个一个的节点。因此在拷贝时需要进行深拷贝,否则拷贝出来的哈希表和原哈希表中存储的都是同一批结点,析构时就会发生析构2次的问题。
    拷贝也就是将原来表中的节点,一个个创建出来,并头插到新表中。

    HashTable(const HashTable& ht)
    {
    	KeyOfT kot;
    	_tables.resize(ht._tables.size());
    	for (int i = 0; i < ht._tables.size(); i++)
    	{
    		Node* cur = ht._tables[i];
    		while (cur)
    		{
    			Node* tmp = new Node(kot(cur->_data));
    			tmp->_next = _tables[i];
    			_tables[i] = tmp;
    			_n++;
    			cur = cur->_next;
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    赋值重载:

    实现赋值重载时,可以借助现代写法。
    传参的时候会产生一次拷贝构造,然后将当前和ht的数据交换即可,完成的是深拷贝。
    当赋值运算符重载函数调用结束后,拷贝构造出来的哈希表会因为出了作用域而被自动析构,此时原哈希表之前的数据也就顺势被释放了。

    // 传参的时候有一次拷贝构造了
    HashTable& operator=(HashTable ht)
    {
    	if (this != &ht)
    	{
    		swap(_tables, ht._tables);
    		swap(_n, ht._n);
    	}
    	return *this;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    析构函数:

    vector虽然能自己调用其析构函数完成析构,但是vector里面的节点是手动new出来,因此也必须手动delete。

    // vector free itself,but the node in the vector need free mannually
    // these nodes are pointer , built-in type
    ~HashTable()
    {
    	for (size_t i = 0; i < _tables.size(); ++i)
    	{
    		// delete every bucket
    		Node* cur = _tables[i];
    		while (cur)
    		{
    			Node* next = cur->_next;
    			delete cur;
    			cur = next;
    		}
    		_tables[i] = nullptr;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    迭代器

    迭代器其实只是一个 HashNode* 的封装。

    注意,迭代器的封装中用到了哈希表的指针,因此需要对 HashTable 进行前置声明。

    operator++ :
    迭代器++的逻辑很简单,就是一个一个桶去遍历,当前节点的 next 不为空,就去下一个节点,为空就说明当前桶走完了,就去下一个桶。

    // forward declaration __HTIterator__ use a pointer of HashTable
    template<class K, class T, class KeyOfT, class HashFunc>
    class HashTable;
    
    template<class K, class T, class KeyOfT, class HashFunc>
    class __HTIterator__
    {
        typedef HashNode<T> Node;
        typedef __HTIterator__<K, T, KeyOfT, HashFunc> Self;
        Node* _node;
        HashTable<K, T, KeyOfT, HashFunc>* _pht;
    public:
        __HTIterator__() {} // deault construction
        __HTIterator__(Node* node, HashTable<K, T, KeyOfT, HashFunc>* pht)
            :_node(node)
            , _pht(pht)
        {}
        Self& operator++()
        {
            if (_node->_next != nullptr)
                _node = _node->_next;
            else // bucket is empty
            {
                KeyOfT kot;
                HashFunc hf;
                size_t hashi = hf(kot(_node->_data)) % _pht->_tables.size();
                hashi++;
                // go to next not-empty bucket 
                for (; hashi < _pht->_tables.size(); ++hashi)
                {
                    if (_pht->_tables[hashi] != nullptr) // find it
                    {
                        _node = _pht->_tables[hashi];
                        break;
                    }
                }
                if (hashi == _pht->_tables.size()) // find not
                    _node = nullptr;
            }
            return *this; // return object itself
        }
        T& operator*()
        {
            return _node->_data;
        }
        T* operator->()
        {
            return &_node->_data;
        }
        bool operator==(const Self& s) const
        {
            return _node == s._node;
        }
        bool operator!=(const Self& s) const
        {
            return _node != s._node;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    HashTable 中,begin 返回的是第一个不为空的桶中的第一个节点。
    迭代器需要用当前节点指针和哈希表指针去构造,也就是 this 指针。

    typedef __HTIterator__<K, T, KeyOfT, HashFunc> iterator;
    
    iterator begin()
    {
        // find the not-empty bucket
        for (size_t i = 0; i < _tables.size(); ++i)
        {
            Node* cur = _tables[i];
            if (cur != nullptr)
                return iterator(cur, this); // convert the pointer of HashTable
        }
        // all empty bucket
        return end();
    }
    
    iterator end()
    {
        return iterator(nullptr, this);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    注意:
    由于正向迭代器中++运算符重载函数在寻找下一个结点时,会访问哈希表中的成员变量_table,而_table成员变量是哈希表的私有成员,因此我们需要将正向迭代器类声明为哈希表类的友元。

    素数表

    素数只能被 1 和它自己整除,这样再去计算 hashi 的时候,取模哈希表的大小,一定程度上能减少哈希冲突。
    因此,有人搞了素数表出来,HashTable 的扩容已素数表中的大小来扩容。

    size_t GetNextPrime(size_t prime)
    {
        const int PRIMECOUNT = 28;
        static const size_t primeList[PRIMECOUNT] =
        {
            53ul, 97ul, 193ul, 389ul, 769ul,
            1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
            49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
            1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul,
            50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,
            1610612741ul, 3221225473ul, 4294967291ul
        };
    
        // get the primeNum larger than prime
        size_t i = 0;
        for (; i < PRIMECOUNT; ++i)
        {
            if (primeList[i] > prime)
                return primeList[i];
        }
    
        return primeList[i];
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    HashTable 源代码

    注意:

    1. 将哈希表中查找函数返回的结点指针,改为返回由结点指针和哈希表地址构成的迭代器。
    2. 将哈希表中插入函数的返回值类型,改为由正向迭代器类型和布尔类型所构成的键值对。
    // unordered_map -> HashTable, MapKeyOfT, HashFunc> _ht;
    // unordered_set -> HashTable _ht;
    template<class K, class T, class KeyOfT, class HashFunc>
    class HashTable
    {
    	typedef HashNode<T> Node;
    
    	template<class K, class T, class KeyOfT, class HashFunc>
    	friend class __HTIterator__;
    public:
    	typedef __HTIterator__<K, T, KeyOfT, HashFunc> iterator;
    
    	iterator begin()
    	{
    		// find the not-empty bucket
    		for (size_t i = 0; i < _tables.size(); ++i)
    		{
    			Node* cur = _tables[i];
    			if (cur != nullptr)
    				return iterator(cur, this); // convert the pointer of HashTable
    		}
    		// all empty bucket
    		return end();
    	}
    
    	iterator end()
    	{
    		return iterator(nullptr, this);
    	}
    
    	// vector free itself,but the node in the vector need free mannually
    	// these nodes are pointer , built-in type
    	~HashTable()
    	{
    		for (size_t i = 0; i < _tables.size(); ++i)
    		{
    			// delete every bucket
    			Node* cur = _tables[i];
    			while (cur)
    			{
    				Node* next = cur->_next;
    				delete cur;
    				cur = next;
    			}
    			_tables[i] = nullptr;
    		}
    	}
    
    	/*HashTable()
    		:_tables(0, nullptr)
    		, _n(0)
    	{}*/
    	// 也可以直接
    	HashTable() = default;
    
    	HashTable(const HashTable& ht)
    	{
    		KeyOfT kot;
    		_tables.resize(ht._tables.size());
    		for (int i = 0; i < ht._tables.size(); i++)
    		{
    			Node* cur = ht._tables[i];
    			while (cur)
    			{
    				Node* tmp = new Node(kot(cur->_data));
    				tmp->_next = _tables[i];
    				_tables[i] = tmp;
    				_n++;
    				cur = cur->_next;
    			}
    		}
    	}
    
    	// 传参的时候有一次拷贝构造了
    	HashTable& operator=(HashTable ht)
    	{
    		if (this != &ht)
    		{
    			swap(_tables, ht._tables);
    			swap(_n, ht._n);
    		}
    		return *this;
    	}
    
    	size_t GetNextPrime(size_t prime)
    	{
    		const int PRIMECOUNT = 28;
    		static const size_t primeList[PRIMECOUNT] =
    		{
    			53ul, 97ul, 193ul, 389ul, 769ul,
    			1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
    			49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
    			1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul,
    			50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,
    			1610612741ul, 3221225473ul, 4294967291ul
    		};
    
    		// get the primeNum larger than prime
    		size_t i = 0;
    		for (; i < PRIMECOUNT; ++i)
    		{
    			if (primeList[i] > prime)
    				return primeList[i];
    		}
    
    		return primeList[i];
    	}
    
    	pair<iterator, bool> Insert(const T& data)
    	{
    		HashFunc hf;
    		KeyOfT kot;
    		iterator pos = Find(kot(data)); // find the same
    		if (pos != end()) // not find --> insert
    			return make_pair(pos, false);
    		// load factor == 1,expand capacity.
    		// that is,one node per bucket on average
    		if (_tables.size() == _n)
    		{
    			//size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
    			size_t newSize = GetNextPrime(_tables.size());
    			if (newSize != _tables.size()) // avert surpass 4294967291ul
    			{
    				vector<Node*> newTables;
    				newTables.resize(newSize, nullptr);
    				for (size_t i = 0; i < _tables.size(); ++i)
    				{
    					Node* cur = _tables[i];
    					while (cur) // head-insert
    					{
    						Node* next = cur->_next;
    						size_t hashi = hf(kot(cur->_data)) % newSize;
    						cur->_next = newTables[hashi];
    						newTables[hashi] = cur;
    
    						cur = next; // update cur
    					}
    					_tables[i] = nullptr;
    				}
    				newTables.swap(_tables);
    			}
    		}
    
    		size_t hashi = hf(kot(data));
    		hashi %= _tables.size();
    		// head-insert to the bucket counterpart
    		Node* newNode = new Node(data);
    		newNode->_next = _tables[hashi];
    		_tables[hashi] = newNode;
    		++_n; // increase size
    
    		return make_pair(iterator(newNode, this), true);
    	}
    
    	iterator Find(const K& key)
    	{
    		if (_tables.size() == 0)
    			return iterator(nullptr, this);
    		KeyOfT kot;
    		HashFunc hf;
    		size_t hashi = hf(key);
    		// size_t hashi = HashFunc()(key); // anonymous object
    		hashi %= _tables.size();
    		Node* cur = _tables[hashi];
    		while (cur)
    		{
    			if (kot(cur->_data) == key)
    				return iterator(cur, this);
    			cur = cur->_next;
    		}
    		return iterator(nullptr, this);
    	}
    
    	bool Erase(const K& key)
    	{
    		if (_tables.size() == 0)
    			return false;
    		HashFunc hf;
    		KeyOfT kot;
    		size_t hashi = hf(key);
    		hashi %= _tables.size();
    		Node* prev = nullptr;
    		Node* cur = _tables[hashi];
    		while (cur)
    		{
    			if (kot(cur->_data) == key)
    			{
    				// prev cur cur->next
    				if (prev == nullptr)
    					_tables[hashi] = cur->_next;
    				else
    					prev->_next = cur->_next;
    				delete cur;
    				--_n;
    				return true;
    			}
    			prev = cur;
    			cur = cur->_next;
    		}
    		return false;
    	}
    private:
    	vector<Node*> _tables;
    	size_t _n = 0;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205

    unordered_set源码

    namespace yzq
    {
    	template<class K, class HashFunc = HashBucketForSetMap::DefaultHash<K>>
    	class unordered_set
    	{
    		struct SetKeyOfT
    		{
    			const K& operator()(const K& key)
    			{
    				return key;
    			}
    		};
    	public:
    		typedef typename HashBucketForSetMap::HashTable<K, K, SetKeyOfT, HashFunc>::iterator iterator;
    
    	public:
    		pair<iterator, bool> insert(const K& key)
    		{
    			return _ht.Insert(key);
    		}
    
    		bool erase(const K& key)
    		{
    			return _ht.Erase(key);
    		}
    
    		iterator begin()
    		{
    			return _ht.begin();
    		}
    
    		iterator end()
    		{
    			return _ht.end();
    		}
    
    
    
    	private:
    		HashBucketForSetMap::HashTable<K, K, SetKeyOfT, HashFunc> _ht;
    	};
    
    	struct Date
    	{
    		Date(int year = 1, int month = 1, int day = 1)
    			:_year(year)
    			, _month(month)
    			, _day(day)
    		{}
    
    		bool operator==(const Date& d) const
    		{
    			return _year == d._year && _month == d._month && _day == d._day;
    		}
    
    		int _year;
    		int _month;
    		int _day;
    	};
    
    	struct DateHashFunc
    	{
    		size_t operator()(const Date& d)
    		{
    			size_t hashi = 0;
    			hashi += d._year;
    			hashi *= 131;
    			hashi += d._month;
    			hashi *= 112;
    			hashi += d._day;
    			return hashi;
    		}
    	};
    
    	void test_set1()
    	{
    		unordered_set<int> s;
    		//set s;
    		s.insert(2);
    		s.insert(3);
    		s.insert(1);
    		s.insert(12);
    		s.insert(2);
    		s.insert(5);
    
    
    		unordered_set<Date, DateHashFunc> sd;
    		sd.insert(Date(2022, 9, 4));
    		sd.insert(Date(2022, 4, 9));
    
    		unordered_set<int>::iterator it = s.begin();
    		//auto it = s.begin(); 
    		while (it != s.end())
    		{
    			cout << *it << " ";
    			++it;
    		}
    		cout << endl; // 1 12 2 3 5
    
    		//for (auto e : s)
    		//{
    		//	cout << e << " ";
    		//}
    		//cout << endl; // 2 3 1 5
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106

    在这里插入图片描述

    2.unordered_map 模拟实现

    unordered_map源码

    #pragma once
    #include "HashTable.h"
    
    namespace yzq
    {
        template<class K, class V, class HashFunc = HashBucketForSetMap::DefaultHash<K>>
        class unordered_map
        {
            struct MapKeyOfT
            {
                const K& operator()(const pair<K, V>& kv)
                {
                    return kv.first;
                }
            };
        public:
            typedef typename HashBucketForSetMap::HashTable<K, pair<K, V>, MapKeyOfT, HashFunc>::iterator iterator;
            pair<iterator, bool> insert(const pair<K, V>& kv)
            {
                return _ht.Insert(kv);
            }
            iterator erase(const pair<K, V>& kv)
            {
                return _ht.Erase(kv);
            }
            iterator find(const K& key)
            {
                return _ht.Find(key);
            }
            iterator begin()
            {
                return _ht.begin();
            }
            iterator end()
            {
                return _ht.end();
            }
            V& operator[](const K& key)
            {
                pair<iterator, bool> ret = insert(make_pair(key, V()));
                return ret.first->second;
            }
        private:
            HashBucketForSetMap::HashTable<K, pair<K, V>, MapKeyOfT, HashFunc> _ht;
        };
        void test_map1()
        {
            unordered_map<string, string> dict;
            dict.insert(make_pair("sort", "排序"));
            dict.insert(make_pair("left", "左边"));
            dict.insert(make_pair("left", "剩余")); // 已经有了left,不能再插进去
            dict["string"]; // 默认是空对象
            dict["left"] = "剩余"; // 这样才是修改
            dict["string"] = "字符串";
    
            unordered_map<string, string>::iterator it = dict.begin();
            while (it != dict.end()) // actually is HashNode*
            {
                cout << it->first << " " << it->second << endl;
                ++it;
            }
    
            for (const auto& kv : dict)
            {
                cout << kv.first << ":" << kv.second << endl;
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

    在这里插入图片描述

    尾声

    🌹🌹🌹

    写文不易,如果有帮助烦请点个赞~ 👍👍👍

    Thanks♪(・ω・)ノ🌹🌹🌹

    😘😘😘

    👀👀由于笔者水平有限,在今后的博文中难免会出现错误之处,本人非常希望您如果发现错误,恳请留言批评斧正,希望和大家一起学习,一起进步ヽ( ̄ω ̄( ̄ω ̄〃)ゝ,期待您的留言评论。
    附GitHub仓库链接

  • 相关阅读:
    C++类模板的重载
    Linux Ubuntu安装配置教程
    Programming Abstractions in C阅读笔记:p306-p307
    未来服务器操作系统的趋势与展望
    【Java SE】this引用注意事项
    Centos7 安装 Mysql
    【桶计数】面试题 01.02. 判定是否互为字符重排
    uni微信小程序隐私协议
    golang中的错误处理
    【面试题】CSS响应式
  • 原文地址:https://blog.csdn.net/a2076188013/article/details/126909433