• 【C++】unordered_set、unordered_map的介绍及使用


    ⭐博客主页:️CS semi主页
    ⭐欢迎关注:点赞收藏+留言
    ⭐系列专栏:C++进阶
    ⭐代码仓库:C++进阶
    家人们更新不易,你们的点赞和关注对我而言十分重要,友友们麻烦多多点赞+关注,你们的支持是我创作最大的动力,欢迎友友们私信提问,家人们不要忘记点赞收藏+关注哦!!!


    一、unordered系列关联式容器

    在C++98中,STL提供了底层为红黑树结构的一系列关联式容器,在查询时效率可达到 l o g 2 N log_2 N log2N,即最差情况下需要比较红黑树的高度次,当树中的节点非常多时,查询效率也不理想。最好的查询是,进行很少的比较次数就能够将元素找到,因此在C++11中,STL又提供了4个unordered系列的关联式容器,这四个容器与红黑树结构的关联式容器使用方式基本类似,只是其底层结构不同

    二、unordered_map and unordered_multimap

    1、unordered_map的介绍

    1. unordered_map是存储键值对的关联式容器,其允许通过keys快速的索引到与其对应的value。
    2. 在unordered_map中,键值通常用于惟一地标识元素,而映射值是一个对象,其内容与此键关联。键和映射值的类型可能不同
    3. 在内部,unordered_map没有对按照任何特定的顺序排序, 为了能在常数范围内找到key所对应的value,unordered_map将相同哈希值的键值对放在相同的桶中。
    4. unordered_map容器通过key访问单个元素要比map快,但它通常在遍历元素子集的范围迭代方面效率较低
    5. unordered_maps实现了直接访问操作符(operator[]),它允许使用key作为参数直接访问value。
    6. 它的迭代器至少是前向迭代器

    2、unordered_map的使用

    (1)定义

    其定义方式如下:

    void test_unordered_map1()
    {
    	// 构造一个空的key为int,value为double的unordered_map
    	unordered_map<int, double> um1;
    
    	// 给um1赋上值
    	um1.insert(make_pair(1, 1.1));
    	um1.insert(make_pair(2, 2.2));
    	um1.insert(make_pair(3, 3.3));
    	um1.insert(make_pair(4, 4.4));
    
    	// 拷贝构造
    	unordered_map<int, double> um2(um1);
    
    	// 迭代器区间拷贝um2的一段
    	unordered_map<int, double> um3(um2.begin(), um2.end());
    
    	// for循环打印一下um3,um3没问题则um1和um2都没问题
    	for (auto& e : um3)
    	{
    		cout << e.first<< "=>" << e.second << " ";
    	}
    	cout << 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

    (2)接口使用

    成员函数功能
    insert插入键值对
    erase删除指定key的值的键值对
    size获取容器中元素的个数
    find查找指定key值的键值对
    empty判断容器是否为空
    clear清空当前容器
    swap交换两个容器中的数据
    count获取容器中指定key值的元素的个数
    []运算符重载的[]功能很强大,有插,改、找等功能
    begin()获取容器中第一个元素的正向迭代器
    end()获取容器中最后一个元素的下一个元素的正向迭代器的

    重点讲一下[]:
    1、若当前容器中已经存在着键值为key的键值对,则返回该键值对value的引用。
    2、若当前容器中没有键值为key的键值对,则先插入键值对,然后再返回该键值对中value的引用。

    下面直接看代码,关于上述所有的代码操作:

    void test_unordered_map2()
    {
    	// 构造一个空的key为int,value为string的unordered_map
    	unordered_map<int, string> um1;
    	
    	// 插入方法一:构造匿名对象插入
    	um1.insert(pair<int, string>(1, "111"));
    	um1.insert(pair<int, string>(2, "222"));
    	um1.insert(pair<int, string>(3, "333"));
    
    	// 插入方法二:调用make_pair插入
    	um1.insert(make_pair(4, "444"));
    	um1.insert(make_pair(5, "555"));
    	um1.insert(make_pair(6, "666"));
    
    	// 插入方法三:用operator[]
    	um1[7] = "777";
    	um1[8] = "888";
    	um1[9] = "999";
    	um1[10] = "000";
    
    	// 遍历方式一:利用迭代器进行遍历打印
    	//unordered_map::iterator it = um1.begin();
    	auto it = um1.begin();
    	while (it != um1.end())
    	{
    		cout << (*it).first << "=>" << (*it).second << " ";
    		++it;
    	}
    	cout << endl; // 1=>111 2=>222 3=>333 4=>444 5=>555 6=>666 7=>777 8=>888 9=>999 10=>000
    
    	// 遍历方法二:利用for循环进行遍历打印
    	for (auto& e : um1)
    	{
    		cout << e.first<< "=>" << e.second << " ";
    	}
    	cout << endl; // 1=>111 2=>222 3=>333 4=>444 5=>555 6=>666 7=>777 8=>888 9=>999 10=>000
    
    	// 删除操作1:根据键值对key删除
    	um1.erase(5);
    	// 删除操作2:根据迭代器进行删除
    	unordered_map<int, string>::iterator rit = um1.find(7); // 顺带使用键值对key就可以用find函数了
    	if (rit != um1.end())
    	{
    		um1.erase(rit);
    	}
    	// 遍历打印一下,用for循环方便快捷一点
    	for (auto& e : um1)
    	{
    		cout << e.first << "=>" << e.second << " ";
    	}
    	cout << endl; // 1=>111 2=>222 3=>333 4=>444 6=>666 8=>888 9=>999 10=>000
    
    	// 修改键值对:通过find获得迭代器进行修改
    	auto pos = um1.find(1);
    	if (pos != um1.end())
    	{
    		pos->second = "11/11";
    	}
    
    	// 修改键值对:通过operator[]运算符重载进行修改
    	um1[2] = "22/22";
    
    	// 打印一下
    	for (auto& e : um1)
    	{
    		cout << e.first << "=>" << e.second << " ";
    	}
    	cout << endl; // 1=>11/11 2=>22/22 3=>333 4=>444 6=>666 8=>888 9=>999 10=>000
    
    	// 判空
    	cout << um1.empty() << endl; // 0 -- 不空
    
    	// 计算容器的大小
    	cout << um1.size() << endl; // 8个
    
    	// 计算容器中键值对的大小
    	cout << um1.count(3) << endl; // 1
    
    	// 交换两容器中的数据
    	unordered_map<int, string> tmp{{11, "123"}, { 22, "345" }};
    	um1.swap(tmp);
    	for (auto& e : tmp)
    	{
    		cout << "tmp=>" << " ";
    		cout << e.first << "=>" << e.second << " ";
    	}
    	cout << endl; // tmp=> 1=>11/11 2=>22/22 3=>333 4=>444 6=>666 8=>888 9=>999 10=>000
    
    	for (auto& e : um1)
    	{
    		cout << "um1=>" << " ";
    		cout << e.first << "=>" << e.second << " ";
    	}
    	cout << endl; // um1=> 11=>123 22=>345
    
    	// 清空
    	um1.clear();
    	for (auto& e : um1)
    	{
    		cout << e.first << "=>" << e.second << " ";
    	}
    	cout << 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
    • 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

    3、unordered_multimap

    这个容器与unordered_map基本一致,这两个的区别在于multimap允许键值对的冗余,也就是可以允许key和value有不同的值。

    void test_unordered_map3()
    {
    	unordered_multimap<int, string> ummp1;
    	ummp1.insert(make_pair(2023, "yes"));
    	ummp1.insert(make_pair(2023, "no"));
    	ummp1.insert(make_pair(2023, "before"));
    	ummp1.insert(make_pair(2023, "now"));
    	for (auto& e : ummp1)
    	{
    		cout << e.first << "=>" << e.second << " ";
    	}
    	cout << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    还有三个不同:

    1、unordered_map和unordered_multimap的find函数:

    find函数功能
    unordered_map返回键值为key的键值对的迭代器
    unordered_multimap返回底层哈希表中第一个找到的键值为key的键值对的迭代器

    2、count函数功能

    count函数功能
    unordered_map键值为key的键值对存在则返回1,不存在则返回0(find成员函数可替代)
    unordered_multimap返回键值为key的键值对的个数(find成员函数不可替代)

    3、operator[]函数功能

    我们在unordered_multimap中是没有这个operator[]重载的,因为这个容器中是可以冗余的,所以我们不确定找的是哪一个,会导致很多的错误,所以我们的unordered_multimap是没有operator[]这个的!

    二、unordered_set and unordered_multiset

    1、unordered_set介绍

    1、unordered_set是不按特定顺序存储键值的关联式容器,其允许通过键值快速的索引到对应的元素。
    2、在unordered_set中,元素的值同时也是唯一地标识它的key。
    3、在内部,unordered_set中的元素没有按照任何特定的顺序排序,为了能在常数范围内找到指定的key,unordered_set将相同哈希值的键值放在相同的桶中。
    4、unordered_set容器通过key访问单个元素要比set快,但它通常在遍历元素子集的范围迭代方面效率较低。
    5、它的迭代器至少是前向迭代器。

    2、unordered_set使用

    (1)定义

    void test_unordered_set1()
    {
    	// 构造一个空壳的us1的unordered_set的容器
    	unordered_set<int> us1;
    
    	// 插入几个值
    	us1.insert(1);
    	us1.insert(2);
    	us1.insert(3);
    	us1.insert(4);
    
    	// 拷贝构造
    	unordered_set<int> us2(us1);
    
    	// 迭代器区间构造
    	unordered_set<int> us3(us2.begin(), us2.end());
    
    	// for循环打印一下
    	for (auto& e : us3)
    	{
    		cout << e << " ";
    	}
    	cout << 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

    (2)接口使用

    成员函数功能
    insert插入指定元素
    erase删除指定元素
    size获取容器中元素的个数
    find查找指定元素
    empty判断容器是否为空
    clear清空当前容器
    swap交换两个容器中的数据
    count获取容器中指定元素的个数
    []运算符重载的[]功能很强大,有插,改、找等功能
    begin()获取容器中第一个元素的正向迭代器
    end()获取容器中最后一个元素的下一个元素的正向迭代器的
    void test_unordered_set2()
    {
    	// 先构造一个空的容器
    	unordered_set<int> us1;
    
    	// 插入元素(只有这一种插入法)
    	us1.insert(1);
    	us1.insert(2);
    	us1.insert(3);
    	us1.insert(1);
    	us1.insert(4);
    	us1.insert(5);
    
    	// 遍历容器第一种方法:迭代器遍历
    	unordered_set<int>::iterator it = us1.begin();
    	while (it != us1.end())
    	{
    		cout << *it << " ";
    		++it;
    	}
    	cout << endl; // 1 2 3 4 5
    
    	// 遍历容器第二种方法:for循环
    	for (auto& e : us1)
    	{
    		cout << e << " ";
    	}
    	cout << endl; // 1 2 3 4 5
    
    	// 删除元素的方式一:直接找到值进行删除
    	us1.erase(1);
    
    	// 删除元素的方法二:利用迭代器进行删除
    	unordered_set<int>::iterator pos = us1.find(2);
    	if (pos != us1.end())
    	{
    		us1.erase(pos);
    	}
    
    	// 打印一下
    	for (auto& e : us1)
    	{
    		cout << e << " ";
    	}
    	cout << endl; // 3 4 5
    
    	// 判断容器是否为空
    	cout << us1.empty() << endl; // 0
    
    	// 获取值为3的个数
    	cout << us1.count(3) << endl; // 1
    
    	// 查看当前容器的容量
    	cout << us1.size() << endl; // 3
    
    	// 交换数据
    	unordered_set<int> tmp{99, 88, 77, 66};
    	us1.swap(tmp);
    
    	// 打印一下
    	for (auto& e : us1)
    	{
    		cout << e << " ";
    	}
    	cout << endl; // 99 88 77 66 
    
    	// 打印一下
    	for (auto& e : tmp)
    	{
    		cout << e << " ";
    	}
    	cout << endl; // 3 4 5
    
    	// 清空
    	us1.clear();
    
    	// 打印一下
    	for (auto& e : us1)
    	{
    		cout << e << " ";
    	}
    	cout << 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
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84

    3、unordered_multiset

    大致实现的功能与unordered_map相同,但唯一不同的一点是在于这个多功能的set是允许值进行重复的!

    void test_unordered_set3()
    {
    	unordered_multiset<int> ums1;
    	ums1.insert(1);
    	ums1.insert(2);
    	ums1.insert(4);
    	ums1.insert(3);
    	ums1.insert(1);
    	ums1.insert(5);
    	ums1.insert(2);
    	ums1.insert(7);
    
    	for (auto& e : ums1)
    	{
    		cout << e << " ";
    	}
    	cout << endl;  // 1 1 2 2 3 4 5 7
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    这个多功能的set是相较于普通set来讲的count函数是返回的个数,而普通set的count函数是如果存在则返回1,不存在则返回0。

    这个多功能set相较于普通set来讲的find函数是返回底层哈希表中第一个找到的键值为val的元素的迭代器,而普通set则是返回简单的key。

    三、map/set 和 unordered_map/unordered_set的区别

    在这里插入图片描述

    性能测试来一波:

    #include 
    #include 
    #include 
    #include 
    using namespace std;
    
    int main()
    {
    	int N = 1000;
    	vector<int> v;
    	v.reserve(N);
    	srand((unsigned int)time(NULL));
    	//随机生成N个数字
    	for (int i = 0; i < N; i++)
    	{
    		v.push_back(rand());
    	}
    
    	//将这N个数插入set容器
    	set<int> s;
    	clock_t begin1 = clock();
    	for (auto e : v)
    	{
    		s.insert(e);
    	}
    	clock_t end1 = clock();
    
    	//将这N个数插入unordered_set容器
    	unordered_set<int> us;
    	clock_t begin2 = clock();
    	for (auto e : v)
    	{
    		us.insert(e);
    	}
    	clock_t end2 = clock();
    
    	//分别输出插入set容器和unordered_set容器所用的时间
    	cout << "set insert: " << end1 - begin1 << endl;
    	cout << "unordered_set insert: " << end2 - begin2 << endl;
    
    	//在set容器中查找这N个数
    	clock_t begin3 = clock();
    	for (auto e : v)
    	{
    		s.find(e);
    	}
    	clock_t end3 = clock();
    
    	//在unordered_set容器中查找这N个数
    	clock_t begin4 = clock();
    	for (auto e : v)
    	{
    		us.find(e);
    	}
    	clock_t end4 = clock();
    
    	//分别输出在set容器和unordered_set容器中查找这N个数所用的时间
    	cout << "set find: " << end3 - begin3 << endl;
    	cout << "unordered_set find: " << end4 - begin4 << endl;
    
    	//将这N个数从set容器中删除
    	clock_t begin5 = clock();
    	for (auto e : v)
    	{
    		s.erase(e);
    	}
    	clock_t end5 = clock();
    
    	//将这N个数从unordered_set容器中删除
    	clock_t begin6 = clock();
    	for (auto e : v)
    	{
    		us.erase(e);
    	}
    	clock_t end6 = clock();
    
    	//分别输出将这N个数从set容器和unordered_set容器中删除所用的时间
    	cout << "set erase: " << end5 - begin5 << endl;
    	cout << "unordered_set erase: " << end6 - begin6 << endl;
    	return 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

    在这里插入图片描述

  • 相关阅读:
    火车头本地文档批量翻译工具
    idea spring boot java maven 依赖重复报错解决
    常见的浏览器跨域解决方法
    Node.js 中的进程和线程
    JAVA计算机毕业设计研究生实验室综合管理系统Mybatis+系统+数据库+调试部署
    阵列信号处理_对比常规波束形成法(CBF)和Capon算法
    和外星人如何交流
    java内嵌浏览器CEF-JAVA、jcef、java chrome
    DHCPsnooping 配置实验(2)
    Java基础 --- 创建线程
  • 原文地址:https://blog.csdn.net/m0_70088010/article/details/133468059