本章我们学习了所有的无序关联式容器:unordered_map容器、unordered_multimap容器、unordered_set容器、unordered_multiset容器,他们都是底层基于哈希表的结构封装起来的一个类。在存储基本数据类型时,键(key)能够通过哈希函数转为哈希值(数据类型为size_t),当我们的键为自定义数据类型时,就需要我们重新定义哈希函数和重载operator'=='运算符。哈希函数一般是通过仿函数来实现的。仿函数重写哈希函数时,要求我们的哈希函数尽量让键均匀分布、键值保持唯一性和方便计算,一般是通过“”位移异或组合法“”来合成哈希值的。下面让我们以unordered_map容器为例,通过它来存放键(key)==user的结构体,重点分析如何重载“==”和重写哈希函数。
读者可想象有这么一个情景:我们有一个用户类(user),成员变量包括姓名(m_s)、年龄(m_age)和性别(m_gender),每个用户对应一个身份码id,程序要求我们快速根据用户来找到对应id,我们就可以充分利用unordered_map容器来满足这个需求。
#include<iostream>
#include<string>
#include<unordered_map>
/*自定义哈希函数*/
using namespace std;
struct user
{
bool operator==(const user&u)const//const不能丢
{
return m_s==u.m_s&&m_age==u.m_age&&m_gender==u.m_gender;
}
user(string s,int age,string gender):m_s(s),m_age(age),m_gender(gender){};
string m_s;
int m_age;
string m_gender;
};
struct fhash
{
size_t operator()(const user&u)const
{
/*组合所有字段的哈希值,让每个成员变量参与分配*/
size_t h1 = hash<string>{}(u.m_s);
size_t h2 = hash<int>{}(u.m_age);
size_t h3 = hash<string>{}(u.m_gender);
/*经典操作*/
/*通过'<<'位移和'^'按位异或,高效返回尽量“独立 ”的哈希值,避免哈希冲突*/
return h1^(h2<<1)^(h3<<2);
}
};
void test()
{
unordered_map<user,int,fhash> ump;
ump.insert({{"dot01",18,"男"},1});
ump.insert({{"dot02",18,"男"},2});
ump.insert({{"dot03",18,"女"},3});
ump.insert({{"dot04",18,"男"},4});
ump.insert({{"dot05",18,"女"},5});
/*遍历我们的unordered_map容器*/
cout << "遍历容器内所有用户:\n" ;
for(auto it = ump.begin();it!=ump.end();++it)
{
cout<< "用户名:【" << it->first.m_s << "】用户年龄: 【" << it->first.m_age << "】用户性别:【" << it->first.m_gender << "】用户识别码:【" << it->second << "】\n";
}
}
int main(){
test();
return 0;
}编译结果如下:

总结:对于无序关联式容器,当键为自定义数据类型时,就需要我们重新定义哈希函数和重载operator'=='运算符,运算符重载不难,但是需要记住位移异或组合法来合成哈希值。
C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:
一点编程也不会写的:零基础C语言学练课程
解决困扰你多年的C语言疑难杂症特性的C语言进阶课程
从零到写出一个爬虫的Python编程课程
只会语法写不出代码?手把手带你写100个编程真题的编程百练课程
信息学奥赛或C++选手的 必学C++课程
蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程
手把手讲解近五年真题的蓝桥杯辅导课程