std::unordered_map配合自定义类型的使用

本文深入探讨C++11中unordered_map的特点与使用,对比map的区别,重点讲解自定义类型的hash函数与比较函数的实现方法。通过具体示例,介绍了如何为自定义类型创建hash函数和比较函数,以及在unordered_map中的调用方式。

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

1.unordered_map内部元素无序,而map是有序的。

2.unordered_map,元素为自定义类型时需要提供自定义的hash函数。为什么?
c++11中,unordered_map定义如下:
这里写图片描述
参数1为传入的key。
参数2为传入的value。
参数3为hash函数,这个函数默认是std::hash,它对内置的类型以及std::string都具有很好的支持,但是不支持自定义类型,故我们需要自定义hash函数为我们自定义的类型。
参数4为比较函数,这个函数默认是std::equal_to,它对内置的类型以及std::string都具有很好的支持,但是不支持自定义类型,需要自定义类型提供对==的重载。
除了从函数定义式来回答为什么需要比较函数外,我们可以从以下进行解释。
即两个相等的value总是导致相同的bucket索引,而不同的value理应导致不同的bucket索引。如果没有比较函数,将导致一个bucket中有多个相同的元素,这样做很浪费,完全没必要。

3.解释了2中的疑问,现在说说怎样自定义hash函数?

从语法的角度,hash函数有以下两种方式,
方式1,(全局函数)
std::size_t customer_hash_func(const T& c)
{
return …
}

方式2,(函数对象)
struct hashFunc
{
std::size_t operator()(const T& c)
{

}
}
4.也可以自定义比较函数(非必须)

比较函数也有以下两种方式,
方式1,(全局函数)
bool eq(const T& c1, const T& c2)
{

}

方式2,(函数对象)
struct equalFunc
{
bool operator()(const T& c1,const T& c2)
{

}
}
以上两种方式均需要T对==的重载,即
class T
{
bool operator==(const T& t1)
{

}
}

4.调用方式。
全局函数方式:
std::unordered_map#T,int,decltype(&customer_hash_func),decltype(&eq)> umap;
函数对象方式:
std::unordered_map#T,int,hashFunc,equalFunc> umap;
或者
std::unordered_map#T,int,hashFunc> umap;
注:为了能正常显示,用#代替<

可参考:
1.https://blog.youkuaiyun.com/yo_bc/article/details/61195377
2.https://blog.youkuaiyun.com/happykocola/article/details/74188452
3.https://blog.youkuaiyun.com/zhangpiu/article/details/49837387
4.https://blog.youkuaiyun.com/whizchen/article/details/9286557

<think>嗯,用户问的是C++中std::unordered_mapmap的区别,包括性能、特点和使用场景。我需要先回忆一下这两个容器的基本结构。 首先,std::map是有序的关联容器,基于红黑树实现,所以元素是按键的顺序存储的。而std::unordered_map则是基于哈希表,元素是无序的。这应该是它们最根本的区别。记得红黑树保证了O(log n)的查找、插入和删除时间,而哈希表平均情况下是O(1),但最坏情况可能到O(n)。所以性能方面,unordered_map的平均情况更好,但map的稳定性更高。 接下来,用户可能关心它们的内部结构。map使用红黑树,每个节点保存键值对,并且保持平衡,所以插入和删除操作不会导致性能大幅波动。而unordered_map使用哈希表,通过哈希函数将键映射到桶里,处理冲突的方法比如链地址法。当哈希函数好的时候,冲突少,性能高,但如果哈希函数不好或者负载因子过高,就会有很多冲突,影响性能。 然后,关于迭代顺序,map的元素是按键升序排列的,所以遍历的时候是有序的。而unordered_map的迭代顺序是不确定的,因为哈希表的存储方式不保证顺序。这点对于需要有序遍历的情况很重要,用户如果需要按键顺序处理元素,应该选择map。 内存占用方面,红黑树的结构可能会有更多的内存开销,每个节点需要维护左右子节点和颜色等信息,而哈希表的内存取决于桶的数量和负载因子。不过具体的内存差异可能需要实际测试才能确定。 在适用场景上,如果需要有序性,或者频繁的范围查询,比如找某个区间内的键,map更合适。而如果只是需要快速的查找、插入和删除,并且不关心顺序,unordered_map更好。例如统计词频,如果不需要按字母顺序输出,用unordered_map效率更高。 另外,哈希表的性能依赖于哈希函数的质量。对于自定义类型作为键,必须提供哈希函数和等于比较函数,而map则需要提供比较函数。比如,如果键是自定义的结构体,使用unordered_map时需要自己实现哈希,而map则需要定义比较运算符。 还有,标准中提到std::map、multimapunordered_map的区别,可能用户还想知道multimap的情况,但当前问题集中在unordered_mapmap,所以暂时不需要扩展。 最后需要总结它们的优缺点,帮助用户根据具体需求选择。比如,对顺序有要求就用map,追求平均性能选unordered_map。同时提醒哈希冲突的影响,以及可能的优化方法,比如调整负载因子或者自定义哈希函数。</think>### C++ std::unordered_map vs std::map 对比分析 #### 1. **底层数据结构** - **std::map** 基于红黑树(平衡二叉搜索树)实现,元素按键的严格弱序(默认升序)排列,所有操作(查找、插入、删除)时间复杂度为 $O(\log n)$[^2]。 - **std::unordered_map** 基于哈希表实现,元素存储顺序与键的哈希值相关,无序。平均情况下查找、插入、删除时间复杂度为 $O(1)$,但最坏情况下可能达到 $O(n)$(如哈希冲突严重时)[^1]。 #### 2. **性能对比** | 特性 | std::map | std::unordered_map | |-------------------|----------------------------|------------------------------| | **查找速度** | $O(\log n)$(稳定) | $O(1)$(平均),$O(n)$(最坏)| | **插入/删除速度** | $O(\log n)$ | $O(1)$(平均) | | **内存占用** | 较高(需维护树结构) | 较低(但哈希表可能预分配桶) | | **迭代顺序** | 按键升序排列 | 无序 | #### 3. **特点与应用场景** - **std::map 适用场景** - 需要元素按**键顺序**遍历(如输出有序结果)。 - 需要**范围查询**(如查找键在 $[a, b]$ 之间的元素)。 - 对性能稳定性要求高(避免哈希冲突导致的性能抖动)。 - **std::unordered_map 适用场景** - 需要**快速查找**且**无需顺序**(如缓存、字典)。 - 键的哈希函数设计良好(减少冲突)。 - 内存敏感且数据规模较大(哈希表通常更紧凑)。 #### 4. **代码示例对比** ```cpp // std::map 示例(有序) std::map<int, std::string> ordered_map; ordered_map[3] = "three"; ordered_map[1] = "one"; // 遍历输出:1 -> "one", 3 -> "three" // std::unordered_map 示例(无序) std::unordered_map<int, std::string> unordered_map; unordered_map[3] = "three"; unordered_map[1] = "one"; // 遍历顺序不确定(可能 3 -> "three", 1 -> "one") ``` #### 5. **其他差异** - **自定义键类型** - `std::map` 需要定义比较运算符(如 `<`)。 - `std::unordered_map` 需要定义哈希函数和 `==` 运算符。 - **内存局部性** 哈希表(`unordered_map`)的访问可能因哈希分布导致缓存不友好,而红黑树(`map`)的遍历更连续。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值