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

news2024/11/30 10:47:23

unordered_set、unordered_map的介绍及使用

  • 一、unordered系列关联式容器
  • 二、unordered_map and unordered_multimap
    • 1、unordered_map的介绍
    • 2、unordered_map的使用
      • (1)定义
      • (2)接口使用
    • 3、unordered_multimap
  • 二、unordered_set and unordered_multiset
    • 1、unordered_set介绍
    • 2、unordered_set使用
      • (1)定义
      • (2)接口使用
    • 3、unordered_multiset
  • 三、map/set 和 unordered_map/unordered_set的区别


一、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是存储<key, value>键值对的关联式容器,其允许通过keys快速的索引到与其对应的value。
  2. 在unordered_map中,键值通常用于惟一地标识元素,而映射值是一个对象,其内容与此键关联。键和映射值的类型可能不同
  3. 在内部,unordered_map没有对<kye, value>按照任何特定的顺序排序, 为了能在常数范围内找到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;
}

(2)接口使用

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

重点讲一下[]:
1、若当前容器中已经存在着键值为key的键值对,则返回该键值对value的引用。
2、若当前容器中没有键值为key的键值对,则先插入键值对<key, value()>,然后再返回该键值对中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<int, string>::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;
}

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、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;
}

(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;  //   
}

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
}

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

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

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

在这里插入图片描述

性能测试来一波:

#include <iostream>
#include <set>
#include <unordered_set>
#include <time.h>
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;
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1054861.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

集合在多线程下安全问题

如果在多线程下&#xff0c;同时操作同一个数据源&#xff0c;就会出现数据安全问题&#xff1a; A线程取出值为10&#xff0c;准备加5. 同时B线程也取出来10&#xff0c;减了5 C取出的时候有可能时15&#xff0c;也有可能时5。产生了数据安全问题。 方法有很多例如&#xff1a…

消息队列-RabbitMQ(二)

接上文《消息队列-RabbitMQ&#xff08;一&#xff09;》 Configuration public class RabbitMqConfig {// 消息的消费方json数据的反序列化Beanpublic RabbitListenerContainerFactory<?> rabbitListenerContainerFactory(ConnectionFactory connectionFactory){Simple…

redis解压+windows安装+无法启动:1067

Redis下载安装图文教程&#xff08;Windows版_超详细&#xff09; 标题若遇到安装后无法启动&#xff1a;1067 排查方法如下&#xff1a; 1.查询是否有服务占用端口 查看6379的端口也没有被占用&#xff08;netstat -ano | findstr :6379&#xff09; 若有&#xff0c;kill掉…

盛最多水的容器 接雨水【基础算法精讲 02】

盛雨水最多的容器 链接 : 11 盛最多水的容器 思路 : 双指针 &#xff1a; 1.对于两条确定的边界&#xff0c;l和r,取中间的线m与r组成容器&#xff0c;如果m的高度>l的高度&#xff0c;那么整个容器的长度会减小&#xff0c;如果低于l的高度&#xff0c;那么不仅高度可…

54、数组--模拟

LCR 146. 螺旋遍历二维数组 给定一个二维数组 array&#xff0c;请返回「螺旋遍历」该数组的结果。 螺旋遍历&#xff1a;从左上角开始&#xff0c;按照 向右、向下、向左、向上 的顺序 依次 提取元素&#xff0c;然后再进入内部一层重复相同的步骤&#xff0c;直到提取完所有…

SpringBoot整合数据库连接

JDBC 1、数据库驱动 JDBC&#xff08;Java DataBase Connectivity&#xff09;&#xff0c;即Java数据库连接。简而言之&#xff0c;就是通过Java语言来操作数据库。 JDBC是sun公司提供一套用于数据库操作的接口. java程序员只需要面向这套接口编程即可。不同的数据库厂商&…

C++八股

1、简述一下C中的多态 在面向对象中&#xff0c;多态是指通过基类的指针或引用&#xff0c;在运行时动态调用实际绑定对象函数的行为&#xff0c;与之相对应的编译时绑定函数称为静态绑定。 静态多态 静态多态是编译器在编译期间完成的&#xff0c;编译器会根据实参类型来选择…

国庆10.1

用select实现服务器并发 ser #include <myhead.h> #define ERR_MSG(msg) do{\fprintf(stderr, "__%d__", __LINE__);\perror(msg);\ }while(0)#define PORT 8888 //端口号&#xff0c;范围1024~49151 #define IP "192.168.1.205" //本机…

ARMv7-A 那些事 - 5.CP15协处理器

By: Ailson Jack Date: 2023.10.01 个人博客&#xff1a;http://www.only2fire.com/ 本文在我博客的地址是&#xff1a;http://www.only2fire.com/archives/157.html&#xff0c;排版更好&#xff0c;便于学习&#xff0c;也可以去我博客逛逛&#xff0c;兴许有你想要的内容呢。…

14:STM32-----看门狗

目录 一:看门狗 1:WDG 2:独立看门狗 (IWDG) A:IWDG框图 B:IWDG_KR键寄存器 C:IWDG超时时间 3:窗口看门狗 (WWDG) A:WWDG框图 B:WWDG工作特性 C:WWDG超时时间 4:独立看门狗和窗口看门狗的区别 5:数据手册 二:案例 A:独立看门狗 1:连接图 2:步骤 3:函数介绍 3:代…

网络爬虫——urllib(2)

前言&#x1f36d; ❤️❤️❤️网络爬虫专栏更新中&#xff0c;各位大佬觉得写得不错&#xff0c;支持一下&#xff0c;感谢了&#xff01;❤️❤️❤️ Python网络爬虫_热爱编程的林兮的博客-CSDN博客 前篇讲解了urllib的基本使用、一个类型六个方法与下载相关内容&#xff0…

《深入浅出OCR》第二章:OCR技术发展与分类

✨专栏介绍: 经过几个月的精心筹备,本作者推出全新系列《深入浅出OCR》专栏,对标最全OCR教程,具体章节如导图所示,将分别从OCR技术发展、方向、概念、算法、论文、数据集等各种角度展开详细介绍。 👨‍💻面向对象: 本篇前言知识主要介绍深度学习知识,全面总结知知识…

Python3数据科学包系列(一):数据分析实战

Python3中类的高级语法及实战 Python3(基础|高级)语法实战(|多线程|多进程|线程池|进程池技术)|多线程安全问题解决方案 Python3数据科学包系列(一):数据分析实战 Python3数据科学包系列(二):数据分析实战 认识下数据科学中数据处理基础包: (1)NumPy 俗话说: 要学会跑需先…

C++核心编程--多态篇

4.7、多态 4.7.1、多态的基本概念 多态是C面向对象三大特征之一 多态分为两类 静态多态&#xff1a;函数重载和运算符重载属于静态多态&#xff0c;复用函数名动态多态&#xff1a;派生类和虚函数实现运行时多态 静态多态和动态多态区别&#xff1a; 静态多态的函数地址早…

Eclipse 主网即将上线迎空投预期,Zepoch 节点或成受益者?

目前&#xff0c;Zepoch 节点空投页面中&#xff0c;模块化 Layer2 Rollup 项目 Eclipse 出现在其空投列表中。 配合近期 Eclipse 宣布了其将由 SVM 提供支持的 Layer2 主网架构&#xff0c;并将在今年年底上线主网的消息后&#xff0c;不免引发两点猜测&#xff1a;一个是 Ecl…

springcloud:四、nacos介绍+启动+服务分级存储模型/集群+NacosRule负载均衡

nacos介绍 nacos是阿里巴巴提供的SpringCloud的一个组件&#xff0c;算是eureka的替代品。 nacos启动 安装过程这里不再赘述&#xff0c;相关安装或启动的问题可以见我的另一篇博客&#xff1a; http://t.csdn.cn/tcQ76 单价模式启动命令&#xff1a;进入bin目录&#xff0…

某房产网站登录RSA加密分析

文章目录 1. 写在前面2. 抓包分析3. 扣加密代码4. 还原加密 1. 写在前面 今天是国庆节&#xff0c;首先祝福看到这篇文章的每一个人节日快乐&#xff01;假期会老的这些天一直在忙事情跟日常带娃&#xff0c;抽不出一点时间来写东西。夜深了、娃也睡了。最近湖南开始降温了&…

SimpleCG动画示例--汉诺塔动画演示

前言 SimpleCG的使用方法在前面已经介绍了许多&#xff0c;有兴趣的同学如果有去动手&#xff0c;制作一些简单动画应该没多大问题的。所以这次我们来演示一下简单动画。我们刚学习C语言的递归函数时&#xff0c;有一个经典例子相信很多同学都写过&#xff0c;那就是汉诺塔。那…

【算法优选】双指针专题——壹

文章目录 &#x1f60e;前言&#x1f334;[移动零](https://leetcode.cn/problems/move-zeroes/)&#x1f6a9;题⽬描述&#xff1a;&#x1f6a9;算法思路&#x1f6a9;算法流程&#x1f6a9;代码实现 &#x1f340;[复写零](https://leetcode.cn/problems/duplicate-zeros/)&…

深入浅出线程池

一、线程 1、什么是线程 线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中&#xff0c;是进程中的实际 运作单位。一条线程指的是进程中一个单一顺序的控制流&#xff0c;一个进程中可以并发多个线程&#xff0c;每条线 程并行执行不同的任务。 2、如…