• 【C++】STL详解(十一)—— unordered_set、unordered_map的介绍及使用


    在这里插入图片描述

    ​📝个人主页@Sherry的成长之路
    🏠学习社区:Sherry的成长之路(个人社区)
    📖专栏链接:C++学习
    🎯长路漫漫浩浩,万事皆有期待

    上一篇博客:【C++】STL详解(五)—— list的介绍及使用

    unordered系列关联式容器

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

    unordered_set的介绍

    unordered_set是不按特定顺序存储键值的关联式容器,其允许通过键值快速的索引到对应的元素。

    在unordered_set中,元素的值同时也是唯一地标识它的key。

    在内部,unordered_set中的元素没有按照任何特定的顺序排序,为了能在常数范围内找到指定的key,unordered_set将相同哈希值的键值放在相同的桶中。

    unordered_set容器通过key访问单个元素要比set快,但它通常在遍历元素子集的范围迭代方面效率较低。

    它的迭代器至少是前向迭代器。

    unordered_set的使用

    unordered_set的定义方式
    方式一: 构造一个某类型的空容器。

    unordered_set<int> us1; //构造int类型的空容器
    
    • 1

    方式二: 拷贝构造某同类型容器的复制品。

    unordered_set<int> us2(us1); //拷贝构造同类型容器us1的复制品
    
    • 1

    方式三: 使用迭代器拷贝构造某一段内容。

    string str("abcedf");
    unordered_set<char> us3(str.begin(), str.end()); //构造string对象某段区间的复制品
    
    • 1
    • 2

    unordered_set接口的使用

    unordered_set当中常用的成员函数如下:

    成员函数	功能
    insert	插入指定元素
    erase	删除指定元素
    find	查找指定元素
    size	获取容器中元素的个数
    empty	判断容器是否为空
    clear	清空容器
    swap	交换两个容器中的数据
    count	获取容器中指定元素值的元素个数
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    unordered_set当中迭代器相关函数如下:

    成员函数	功能
    begin	获取容器中第一个元素的正向迭代器
    end	    获取容器中最后一个元素下一个位置的正向迭代器
    
    • 1
    • 2
    • 3

    使用示例:

    #include 
    #include 
    using namespace std;
    
    int main()
    {
    	unordered_set<int> us;
    	//插入元素(去重)
    	us.insert(1);
    	us.insert(4);
    	us.insert(3);
    	us.insert(3);
    	us.insert(2);
    	us.insert(2);
    	us.insert(3);
    	//遍历容器方式一(范围for)
    	for (auto e : us)
    	{
    		cout << e << " ";
    	}
    	cout << endl; //1 4 3 2
    	//删除元素方式一
    	us.erase(3);
    	//删除元素方式二
    	unordered_set<int>::iterator pos = us.find(1); //查找值为1的元素
    	if (pos != us.end())
    	{
    		us.erase(pos);
    	}
    	//遍历容器方式二(迭代器遍历)
    	unordered_set<int>::iterator it = us.begin();
    	while (it != us.end())
    	{
    		cout << *it << " ";
    		it++;
    	}
    	cout << endl; //4 2
    	//容器中值为2的元素个数
    	cout << us.count(2) << endl; //1
    	//容器大小
    	cout << us.size() << endl; //2
    	//清空容器
    	us.clear();
    	//容器判空
    	cout << us.empty() << endl; //1
    	//交换两个容器的数据
    	unordered_set<int> tmp{ 11, 22, 33, 44 };
    	us.swap(tmp);
    	for (auto e : us)
    	{
    		cout << e << " ";
    	}
    	cout << endl; //11 22 33 44
    	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

    unordered_multiset

    unordered_multiset容器与unordered_set容器的底层数据结构是一样的,都是哈希表,其次,它们所提供的成员函数的接口都是基本一致的,这里就不再列举了,这两种容器唯一的区别就是,unordered_multiset容器允许键值冗余,即unordered_multiset容器当中存储的元素是可以重复的。

    #include 
    #include 
    using namespace std;
    
    int main()
    {
    	unordered_multiset<int> ums;
    	//插入元素(允许重复)
    	ums.insert(1);
    	ums.insert(4);
    	ums.insert(3);
    	ums.insert(3);
    	ums.insert(2);
    	ums.insert(2);
    	ums.insert(3);
    	for (auto e : ums)
    	{
    		cout << e << " ";
    	}
    	cout << endl; //1 4 3 3 3 2 2
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    由于unordered_multiset容器允许键值冗余,因此该容器中成员函数find和count的意义与unordered_set容器中的也有所不同:

    成员函数find 功能

    unordered_set容器		返回键值为val的元素的迭代器
    unordered_multiset容器	返回底层哈希表中第一个找到的键值为val的元素的迭代器
    
    • 1
    • 2

    成员函数count 功能

    unordered_set容器		键值为val的元素存在则返回1,不存在则返回0(find成员函数可替代)
    unordered_multiset容器	返回键值为val的元素个数(find成员函数不可替代)
    
    • 1
    • 2

    unordered_map的介绍

    unordered_map是存储键值对的关联式容器,其允许通过key值快速的索引到与其对应是value。

    在unordered_map中,键值通常用于唯一地标识元素,而映射值是一个对象,其内容与此键关联。键和映射值的类型可能不同。

    在内部,unordered_map没有对按照任何特定的顺序排序,为了能在常数范围内找到key所对应的value,unordered_map将相同哈希值的键值对放在相同的桶中。

    unordered_map容器通过key访问单个元素要比map快,但它通常在遍历元素子集的范围迭代方面效率较低。

    unordered_map实现了直接访问操作符(operator[]),它允许使用key作为参数直接访问value。

    它的迭代器至少是前向迭代器。

    unordered_map的使用

    unordered_map的定义方式

    方式一: 指定key和value的类型构造一个空容器。

    unordered_map<int, double> um1; //构造一个key为int类型,value为double类型的空容器
    
    • 1

    方式二: 拷贝构造某同类型容器的复制品。

    unordered_map<int, double> um2(um1); //拷贝构造同类型容器um1的复制品
    
    • 1

    方式三: 使用迭代器拷贝构造某一段内容。

    unordered_map<int, double> um3(um2.begin(), um2.end()); //使用迭代器拷贝构造um2容器某段区间的复制品
    
    • 1

    unordered_map接口的使用

    unordered_map当中常用的成员函数如下:

    成员函数	功能
    insert	插入键值对
    erase	删除指定key值的键值对
    find	查找指定key值的键值对
    size	获取容器中元素的个数
    empty	判断容器是否为空
    clear	清空容器
    swap	交换两个容器中的数据
    count	获取容器中指定key值的元素个数
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    除了上述的成员函数之外,unordered_map容器当中还实现了[ ]运算符重载函数,该重载函数的功能非常强大:

    若当前容器中已有键值为key的键值对,则返回该键值对value的引用。
    若当前容器中没有键值为key的键值对,则先插入键值对,然后再返回该键值对中value的引用。

    unordered_map当中迭代器相关函数如下:

    成员函数		功能
    begin		获取容器中第一个元素的正向迭代器
    end			获取容器中最后一个元素下一个位置的正向迭代器
    
    • 1
    • 2
    • 3

    使用示例:

    #include 
    #include 
    #include 
    using namespace std;
    
    int main()
    {
    	unordered_map<int, string> um;
    	//插入键值对方式一:构造匿名对象插入
    	um.insert(pair<int, string>(1, "one"));
    	um.insert(pair<int, string>(2, "two"));
    	um.insert(pair<int, string>(3, "three"));
    	//遍历方式一:范围for
    	for (auto e : um)
    	{
    		cout << e.first << "->" << e.second << " ";
    	}
    	cout << endl; //1->one 2->two 3->three
    	//插入键值对方式二:调用make_pair函数模板插入
    	um.insert(make_pair(4, "four"));
    	um.insert(make_pair(5, "five"));
    	um.insert(make_pair(6, "six"));
    	//遍历方式二:迭代器遍历
    	unordered_map<int, string>::iterator it = um.begin();
    	while (it != um.end())
    	{
    		cout << it->first << "->" << it->second << " ";
    		it++;
    	}
    	cout << endl; //1->one 2->two 3->three 4->four 5->five 6->six
    	//插入键值对方式三:利用[]运算符重载函数进行插入(常用)
    	um[7] = "seven";
    	um[8] = "eight";
    	um[9] = "nine";
    	for (auto e : um)
    	{
    		cout << e.first << "->" << e.second << " ";
    	}
    	cout << endl; //9->nine 1->one 2->two 3->three 4->four 5->five 6->six 7->seven 8->eight
    	//删除键值对方式一:根据key值删除
    	um.erase(5);
    	//删除键值对方式二:根据迭代器删除
    	unordered_map<int, string>::iterator pos = um.find(7); //查找键值为7的键值对
    	if (pos != um.end())
    	{
    		um.erase(pos);
    	}
    	for (auto e : um)
    	{
    		cout << e.first << "->" << e.second << " ";
    	}
    	cout << endl; //9->nine 1->one 2->two 3->three 4->four 6->six 8->eight
    	//修改键值对方式一:通过find获得迭代器,通过迭代器修改
    	pos = um.find(1);
    	if (pos != um.end())
    	{
    		pos->second = "one/first";
    	}
    	//修改键值对方式二:利用[]运算符重载函数进行修改(常用)
    	um[2] = "two/second";
    	for (auto e : um)
    	{
    		cout << e.first << "->" << e.second << " ";
    	}
    	cout << endl; //9->nine 1->one/first 2->two/second 3->three 4->four 6->six 8->eight
    	//容器中key值为3的键值对的个数
    	cout << um.count(3) << endl;
    	//容器的大小
    	cout << um.size() << endl;
    	//清空容器
    	um.clear();
    	//容器判空
    	cout << um.empty() << endl;
    	//交换两个容器的数据
    	unordered_map<int, string> tmp{ { 2021, "before" }, { 2022, "now" } };
    	um.swap(tmp);
    	for (auto e : um)
    	{
    		cout << e.first << "->" << e.second << " ";
    	}
    	cout << endl; //2021->before 2022->now
    	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
    • 82
    • 83

    unordered_multimap

    unordered_multimap容器与unordered_map容器的底层数据结构是一样的,都是哈希表,其次,它们所提供的成员函数的接口都是基本一致的,这里就不再列举了,这两种容器唯一的区别就是,unordered_multimap容器允许键值冗余,即unordered_multimap容器当中存储的键值对的key值是可以重复的。

    #include 
    #include 
    #include 
    using namespace std;
    
    int main()
    {
    	unordered_multimap<int, string> umm;
    	//插入键值对(允许键值重复)
    	umm.insert(make_pair(2022, "吃饭"));
    	umm.insert(make_pair(2022, "睡觉"));
    	umm.insert(make_pair(2022, "敲代码"));
    	for (auto e : umm)
    	{
    		cout << e.first << "->" << e.second << " ";
    	}
    	cout << endl; //2022->吃饭 2022->睡觉 2022->敲代码
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    由于unordered_multimap容器允许键值对的键值冗余,因此该容器中成员函数find和count的意义与unordered_map容器中的也有所不同:

    成员函数find 功能

    unordered_map容器		返回键值为key的键值对的迭代器
    unordered_multimap容器	返回底层哈希表中第一个找到的键值为key的键值对的迭代器
    
    • 1
    • 2

    成员函数count 功能

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

    其次,由于unordered_multimap容器允许键值对的键值冗余,调用[ ]运算符重载函数时,应该返回键值为key的哪一个键值对的value的引用存在歧义,因此在unordered_multimap容器当中没有实现[ ]运算符重载函数。

    总结:

    今天我们比较详细地完成了 unordered_set、unordered_map的介绍及使用,了解了一些有关的底层原理。接下来,我们用哈希表封装出unordered_map和unordered_set。希望我的文章和讲解能对大家的学习提供一些帮助。

    当然,本文仍有许多不足之处,欢迎各位小伙伴们随时私信交流、批评指正!我们下期见~

    在这里插入图片描述

  • 相关阅读:
    SpringBoot 学习(八)异步任务,邮件发送和定时执行
    基础DFS与BFS
    隐秘而伟大——纪念图灵诞辰110周年
    pinia不同于vuex的状态管理器
    04-Jenkins构建Maven项目
    内网IP端口提供外网连接访问?快解析动态域名与内网映射P2P穿透方案
    PAT A1017 Queueing at Bank
    java基础巩固11
    Dubbo3应用开发—Dubbo直连开发相关概念:通信、协议、序列化
    实时数据的处理一致性如何保证?
  • 原文地址:https://blog.csdn.net/m0_73258399/article/details/133559910