C++效率掌握之STL库:unordered_map && unordered_set底层剖析

看了前面的底层封装后,其实封装的过程及方法都大差不差,unordered_map && unordered_set 也是如此,所以本篇就简单提及一些细节,具体最详细的一些部分可以去看前面的文章

传送门:C++效率掌握之STL库:list底层剖析及迭代器万字详解

传送门:C++效率掌握之STL库:map && set底层剖析及迭代器万字详解

1.unordered_map、unordered_set的基本结构

🚩unordered_set:

template<class K, class V>
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;
};

🚩unordered_map:

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 虽然事 k-k 类型,unordered_mapk-v 类型,但是实际上这两个类共用一个哈希表,准确来说是共用同一个模板类型,unordered_set<K,K>unordered_map<K,pair<K,V>>

2.普通迭代器

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, HashTable<K, T, KeyOfT, HashFunc>* pht)
		:_node(node)
		,_pht(pht)
	{}*/

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

	// 普通迭代器时,他是拷贝构造
	// 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;
	}
};

typedef HTIterator<K, T, T*, T&, KeyOfT, HashFunc> Iterator 和构造函数是为 Insert 操作准备的,因为涉及到普通迭代器创建 const 迭代器

🔥值得注意的是: 有人说不是可以通过权限转化吗?但是权限转化是只有涉及引用和指针的类型时才会生效,而这里是模板参数之间的转化

3.const迭代器

unordered_set 本身无论是 const 迭代器还是普通迭代器都会被 typedefconst_iterator

对于 unordered_map 来说,key 是不允许改变的,value 是可以改变的,但是如果像 set 那样写的话 keyvalue 都不能修改了,所以直接在 pairkeyconst ,控制 value 即可

4.insert返回值 operator[]

🚩unordered_set:

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

🚩unordered_map:

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

这里的转化尤为复杂,一定要看之前文章的详细解析

希望读者们多多三连支持

小编会继续更新

你们的鼓励就是我前进的动力!

请添加图片描述

<think>好的,我需要回答用户关于std::unordered_map和std::unordered_set删除元素效率的问题。首先,我应该回顾这两种容器的底层实现,因为它们都是基于哈希表的,所以删除操作的效率可能相似,但可能有细微差别。 根据引用[5],这两个容器都支持通过迭代器、键值或范围来删除元素。对于unordered_map,删除时需要键值对,而unordered_set只需要元素值。不过,它们的erase方法在底层实现上应该类似,因为都是哈希表的操作。 接下来,哈希表的删除操作通常是平均O(1)时间复杂度,最坏情况下O(n),比如当哈希冲突严重时。但用户可能更关心平均情况下的效率差异。由于unordered_map存储的是键值对,而unordered_set只有键,可能在删除时处理的数据量稍有不同,但影响可能不大。 另外,引用[4]提到unordered_set用于检查存在性或唯一性,而unordered_map用于键值对存储。所以,删除元素时,unordered_map需要处理键和值,而unordered_set仅处理键。但哈希表的节点结构可能不同,导致内存操作上的差异,但具体实现会影响效率,比如内存分配和释放的开销。 需要考虑哈希函数计算和桶查找的时间,这部分两者应该相同,因为都是基于键的哈希值定位桶。删除元素后,链表的调整操作在两种容器中也是类似的,所以时间差异可能不明显。 不过,用户可能想知道实际性能差异,可能需要参考基准测试或具体实现细节。根据STL的实现,比如GCC或Clang,它们的unordered_mapunordered_set在删除操作上的效率是否接近,或者是否有优化差异。例如,unordered_map的节点可能更大,涉及更多的内存操作,但哈希表操作本身可能主导时间,所以整体差异可能不大。 总结来说,两者的删除操作在平均情况下都是O(1),最坏O(n)。实际效率差异可能取决于具体场景和数据量,但通常不会有显著区别。如果键的哈希分布良好,两种容器的删除效率相近。用户应根据需要选择容器类型,而非基于删除效率。</think>在C++中,`std::unordered_map`和`std::unordered_set`的删除操作均基于哈希表实现,其效率特性对比如下: ### 1. **时间复杂度分析** - **平均情况**:两者的删除操作时间复杂度均为$O(1)$。通过键(或元素)的哈希值直接定位桶(bucket),随后在链表中查找并删除目标节点。 - **最坏情况**:若哈希冲突严重(例如所有元素哈希到同一桶),时间复杂度退化为$O(n)$,需遍历链表完成删除[^4][^5]。 ### 2. **实现差异对效率的影响** - **存储结构**: - `std::unordered_map`存储键值对,每个节点包含键和值,内存占用略大。 - `std::unordered_set`仅存储键,节点更小,内存局部性可能更优。 - **删除操作开销**: - `unordered_map`需处理键值对的析构,而`unordered_set`仅处理键的析构。若值类型包含复杂资源释放,可能略微增加时间开销。 - 实际性能差异通常微小,因哈希表操作(哈希计算、桶定位)占主导[^5]。 ### 3. **删除方式与性能** 两种容器均支持以下删除接口: - **通过迭代器删除**:直接操作节点,效率最高。 - **通过键删除**:需先查找键,再执行删除,额外包含一次哈希计算和桶定位。 - **范围删除**:删除连续迭代器范围,时间复杂度与范围大小成线性关系。 ### 4. **实际场景建议** - **优先选择容器类型**:根据需求决定: - 若需关联键和值,使用`std::unordered_map`[^4]。 - 若仅需判断元素存在性,使用`std::unordered_set`。 - **优化哈希函数**:减少冲突可避免最坏情况,提升删除效率。 - **预分配桶数量**:减少动态扩容带来的哈希表重组开销[^2][^3]。 ### 代码示例 ```cpp // unordered_map 删除示例 std::unordered_map<int, std::string> umap = {{1, "a"}, {2, "b"}}; umap.erase(1); // 通过键删除,平均O(1) // unordered_set 删除示例 std::unordered_set<int> uset = {1, 2, 3}; uset.erase(2); // 通过键删除,平均O(1) ``` ### 总结 `std::unordered_map`和`std::unordered_set`的删除操作效率在**平均情况下相同**,均为$O(1)$;**实际性能差异可忽略**,选择容器时应更关注功能需求而非删除效率
评论 56
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值