std::hash_map的使用

本文详细介绍了std::hash_map的数据结构定义及哈希比较器的工作原理,并通过示例代码展示了如何自定义比较器来使用std::hash_map。同时,对比了std::hash_map与std::map的时间复杂度和适用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

std::hash_map的使用(基于VC2010)

 

std::hash_map的定义

// std::hash_map的定义

template<class _Kty,	// Key
	class _Ty,			// Value
	class _Tr = hash_compare<_Kty, less<_Kty> >,	// 哈希比较器(里面也调用了普通的比较器)
	class _Alloc = allocator<pair<const _Kty, _Ty> > >	// 内存分配器
	class hash_map : public _Hash<_Hmap_traits<_Kty, _Ty, _Tr, _Alloc, false> >	// 父类(哈希)
{
......
}


std::hash_map的哈希比较器

// std::hash_map的哈希比较器

// 哈希比较器里面应该与哈希算法相关

// 哈希比较器里面还是使用了和std::map一样的默认的less比较器.

template<class _Kty, class _Pr = less<_Kty> >
class hash_compare
{	// traits class for hash containers
public:
	enum
	{	// parameters for hash table
		bucket_size = 1		// 0 < bucket_size
	};
	
	// construct with default comparator
	hash_compare() : comp()
	{}
	
	// construct with _Pred comparator
	hash_compare(_Pr _Pred) : comp(_Pred)
	{}

	// Hash function
	// 该函数的实现不是很懂(应该是哈希算法的一部分, 桶之类的), 暂时不深究
	size_t operator()(const _Kty& _Keyval) const
	{	// hash _Keyval to size_t value by pseudorandomizing transform
		long _Quot = (long)(hash_value(_Keyval) & LONG_MAX);
		ldiv_t _Qrem = _CSTD ldiv(_Quot, 127773);

		_Qrem.rem = 16807 * _Qrem.rem - 2836 * _Qrem.quot;
		if (_Qrem.rem < 0)
			_Qrem.rem += LONG_MAX;
		return ((size_t)_Qrem.rem);
	}

	bool operator()(const _Kty& _Keyval1, const _Kty& _Keyval2) const
	{	// test if _Keyval1 ordered before _Keyval2
		return (comp(_Keyval1, _Keyval2));					// 这里最终还是使用了less比较器
	}

protected:
	_Pr comp;	// the comparator object
};

 

使用

// 看使用代码

#include "stdafx.h"
#include <string>
#include <hash_map>

// 定义自己std::map比较器
typedef std::string* PSTDString;

template<class _Ty>
struct PLess	
{
	// functor for operator<
	bool operator()(const _Ty& pLeft, const _Ty& pRight) const
	{
		// apply operator< to operands
		return (*pLeft) < (*pRight);		// 这个比较器与默认比较器不同的地方
	}
};

int _tmain(int argc, _TCHAR* argv[])
{
	// 这里还是使用std::hash_map的std::hash_compare. 但是
	// std::hash_compare内部最终的比较使用自定义的比较器.
	std::hash_map<PSTDString, int, std::hash_compare<PSTDString, PLess<PSTDString> > > PStringToIntMap1;

	// 这里的使用和std::map一样
	std::string str1 = "1";
	std::string str2 = "2";
	std::string str3 = "3";
	PStringToIntMap1[&str1] = 3;
	PStringToIntMap1[&str2] = 2;
	PStringToIntMap1[&str3] = 1;

	return 0;
}

四. 与std::map的比较

std::hash_map的时间复杂度是O(1) -- O(N) : 不稳定, 但比map快.

std::map的时间复杂度是O(log2N) : 比hash_map慢, 但稳定.
看你的使用场合. STL中使用上完全可以互相替换.

<think>我们被要求提供关于`std::unordered_map`的使用示例及常见问题。根据引用内容,我们已经知道`std::unordered_map`是一个基于哈希表的关联容器,它存储键值对,并且提供快速的查找、插入和删除操作(平均时间复杂度为O(1))。同时,我们也了解到它不保证元素的顺序。下面我们将通过几个示例来展示`std::unordered_map`的基本用法,并讨论一些常见问题。###基本使用示例首先,我们需要包含头文件:```cpp#include<unordered_map>#include<iostream>#include<string>```####示例1:创建和插入元素```cppintmain(){//创建一个unordered_map,键为std::string类型,值为int类型std::unordered_map<std::string,int>word_count;//插入元素:使用下标操作符word_count["apple"]=1;//如果"apple"不存在,则会插入,值为1;如果存在,则修改值//插入元素:使用insert函数word_count.insert(std::make_pair("banana",2));//插入元素:使用emplace(C++11)word_count.emplace("orange",3);//输出元素for(constauto&pair:word_count){std::cout<<pair.first<<":"<<pair.second<<'\n';}return0;}```####示例2:查找元素```cppintmain(){std::unordered_map<std::string,int>word_count={{"apple",1},{"banana",2},{"orange",3}};//使用find查找元素autoit=word_count.find("apple");if(it!=word_count.end()){std::cout<<"Foundapple:"<<it->second<<'\n';}else{std::cout<<"Applenotfound\n";}//使用count(返回0或1,因为unordered_map中键唯一)if(word_count.count("banana")>0){std::cout<<"Bananaexists\n";}//使用at(如果键不存在会抛出异常)try{intcount=word_count.at("orange");std::cout<<"Orange:"<<count<<'\n';}catch(conststd::out_of_range&e){std::cout<<"Orangenotfound\n";}return0;}```####示例3:删除元素```cppintmain(){std::unordered_map<std::string,int>word_count={{"apple",1},{"banana",2},{"orange",3}};//通过键删除word_count.erase("apple");//通过迭代器删除autoit=word_count.find("banana");if(it!=word_count.end()){word_count.erase(it);}//删除所有元素//word_count.clear();//输出剩余元素for(constauto&pair:word_count){std::cout<<pair.first<<":"<<pair.second<<'\n';}return0;}```###常见问题及注意事项1.**哈希冲突与性能**`std::unordered_map`使用哈希表实现,当哈希冲突较多时,性能会下降(最坏情况O(n))。可以通过提供自定义哈希函数和自定义相等比较函数来优化。2.**自定义类型作为键**如果键是自定义类型,需要提供哈希函数和相等比较函数。例如:```cppstructMyKey{intid;std::stringname;};//自定义哈希函数structMyKeyHash{std::size_toperator()(constMyKey&k)const{returnstd::hash<int>()(k.id)^(std::hash<std::string>()(k.name)<<1);}};//自定义相等比较structMyKeyEqual{booloperator()(constMyKey&lhs,constMyKey&rhs)const{returnlhs.id==rhs.id&&lhs.name==rhs.name;}};std::unordered_map<MyKey,int,MyKeyHash,MyKeyEqual>my_map;```在C++11之后,也可以使用特化`std::hash`模板的方式:```cppnamespacestd{template<>structhash<MyKey>{size_toperator()(constMyKey&k)const{returnhash<int>()(k.id)^(hash<string>()(k.name)<<1);}};}//注意:还需要提供operator==的重载booloperator==(constMyKey&lhs,constMyKey&rhs){returnlhs.id==rhs.id&&lhs.name==rhs.name;}//然后就可以直接使用std::unordered_map<MyKey,int>my_map;```3.**迭代顺序**`std::unordered_map`中的元素是无序的,因此遍历时顺序是不确定的。如果需要有序,应使用`std::map`。4.**内存使用**哈希表通常需要较大的内存来避免冲突,因此内存占用比`std::map`(红黑树实现)更高。5.**性能考虑**-对于查找密集型操作,`std::unordered_map`通常更快(平均O(1))。-对于需要有序遍历或内存敏感的场景,`std::map`(O(logn))可能更合适。6.**桶接口**`std::unordered_map`提供了一系列桶接口,用于查看和调整哈希表的性能:```cpp//桶的数量std::cout<<"Bucketcount:"<<word_count.bucket_count()<<'\n';//负载因子(元素数/桶数)std::cout<<"Loadfactor:"<<word_count.load_factor()<<'\n';//设置最大负载因子,超过则会重新哈希word_count.max_load_factor(0.7);//调整桶的数量(重新哈希)word_count.rehash(100);```7.**迭代器失效**-插入操作可能导致迭代器失效(如果引起重新哈希)。-删除操作会使指向被删除元素的迭代器失效,其他迭代器不受影响。###总结`std::unordered_map`是一个高效的关联容器,适用于需要快速查找的场景。使用时需注意键类型的哈希函数和相等比较,以及可能的性能问题。§§相关问题§§1.如何为`std::unordered_map`提供自定义哈希函数?2.在什么情况下`std::map`比`std::unordered_map`更合适?3.如何避免`std::unordered_map`的重新哈希导致的性能问题?4.如何遍历`std::unordered_map`的桶?</think>### `std::unordered_map` 使用指南与常见问题 `std::unordered_map` 是 C++ 标准库中的哈希表实现,提供 O(1) 平均时间复杂度的查找、插入和删除操作[^2]。下面通过示例和常见问题解析其用法: --- #### 基础使用示例 ```cpp #include <iostream> #include <unordered_map> #include <string> int main() { // 创建 unordered_map std::unordered_map<std::string, int> wordCount; // 插入元素(三种方式) wordCount["apple"] = 5; // 下标插入 wordCount.insert({"banana", 3}); // insert插入 wordCount.emplace("cherry", 7); // emplace高效插入 // 遍历元素(无序输出) for (const auto& pair : wordCount) { std::cout << pair.first << ": " << pair.second << '\n'; } /* 可能输出: cherry: 7 banana: 3 apple: 5 */ // 查找元素 if (auto it = wordCount.find("apple"); it != wordCount.end()) { std::cout << "Found apple: " << it->second << '\n'; } // 删除元素 wordCount.erase("banana"); // 检查存在性 std::cout << "Has banana? " << wordCount.count("banana") << '\n'; // 输出0 return 0; } ``` --- #### 自定义键类型 当键为自定义类型时,需提供**哈希函数**和**相等比较函数**: ```cpp struct Point { int x, y; bool operator==(const Point& p) const { return x == p.x && y == p.y; } }; // 自定义哈希函数 struct PointHash { size_t operator()(const Point& p) const { return std::hash<int>()(p.x) ^ (std::hash<int>()(p.y) << 1); } }; int main() { std::unordered_map<Point, std::string, PointHash> pointMap; pointMap[{1, 2}] = "A"; pointMap[{3, 4}] = "B"; std::cout << pointMap.at({1, 2}); // 输出 "A" } ``` --- #### 性能优化技巧 1. **预分配桶数量** ```cpp std::unordered_map<int, int> map; map.reserve(1000); // 预分配空间,减少重新哈希 ``` 2. **调整负载因子** ```cpp map.max_load_factor(0.7); // 设置最大负载因子 map.rehash(2000); // 强制重新哈希 ``` 3. **使用局部引用避免拷贝** ```cpp auto& count = wordCount["apple"]; // 获取引用 count += 10; // 直接修改值 ``` --- ### 常见问题与解决方案 1. **迭代顺序不可预测** `std::unordered_map` 的元素存储顺序由哈希值决定[^2]。若需有序遍历,改用 `std::map`。 2. **自定义类型哈希冲突** 确保自定义哈希函数分布均匀: ```cpp // 改进的哈希函数(使用标准库组合) struct BetterPointHash { size_t operator()(const Point& p) const { return std::hash<int>()(p.x) ^ (std::hash<int>()(p.y) << 16); } }; ``` 3. **插入时键重复** 使用 `insert` 会保留原值,`operator[]` 会覆盖旧值: ```cpp wordCount.insert({"apple", 10}); // 失败,保留原值5 wordCount["apple"] = 10; // 成功,值变为10 ``` 4. **查找性能下降** 当负载因子过高时,操作可能退化为 O(n)[^3]。解决方案: - 监控负载因子:`map.load_factor()` - 提前调用 `reserve()` 预分配空间 5. **迭代器失效** 插入/删除操作可能导致迭代器失效: ```cpp auto it = map.begin(); map.erase(it); // 此后it不可用 ``` --- #### 适用场景建议 | 场景 | 推荐容器 | 原因 | |-----------------------|-------------------|--------------------------| | 高频查找/更新 | `unordered_map` | O(1) 平均时间复杂度[^3] | | 需要有序遍历 | `std::map` | 红黑树保证有序 | | 内存敏感场景 | `std::map` | 哈希表内存开销更大[^3] | | 自定义类型键 | 两者均可 | 需提供比较/哈希函数 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值