30. 全面解析STL关联式容器:map、multimap、set、multiset及其无序版本详解

1.前言

紧接前面章节的序列化容器,本章开始讲解STL中的关联式容器。

关联式容器与上面的序列式容器最大的区别就是,序列式容器是按顺序的存储所有元素,访问元素的方式是通过下标,或遍历

而关联式容器则采用给每一个分配一个,我们访问元素的方式就是直接通过,就能得到它的,总结来说就是,关联式容器存储的是一个键值对,而序列式容器只存储

2.pair

在了解各个关联式容器之前, 我们必须来看一看各个容器存储的基本单元:键值对,即这里介绍的pair

它的使用方法非常简单:

#include<iostream>
#include<utility> //pair所在头文件
using namespace std;
int main() {
	pair<int, string> p;
	p.first = 10;
	p.second = "csdn";
}

首先我们需要理解一下它的实现过程。

因为它是用来存储两个变量的,所以它的模板参数有两个,分别指定即可,其中第一个为 的类型,第二个为的类型,我这里指定的键为int,值为string类型。

然后我们就可以直接通过它的first属性与second属性来分别给键与值进行赋值。

这基本就是它的所有用法,但如果在容器中添加一个pair元素,每次都这样写的话,还是有点麻烦的。

所以可以直接初始化:

pair<int, string> p{10,"www.kucoding.com"};

为了更加便捷的生成一个pair,还提供了一个函数来生成pair:

make_pair<int, string>(10, "www.kucoding.com");

它可以直接返回一个生成好的pair,类型为intstring组成的键值对。

但还是太长了, 所以我们还可以进一步简化:

make_pair(10, "www.kucoding.com");

你可以不用填入两个模板参数,它可以根据你填入的10与字符串"www.kucoding.com"自动推断出类型,使用起来更加简单!

3.map

这个容器应该是我们最常用的容器之一,它的特点就是不可重复,也不能被修改,并且默认会根据你的键值进行升序排序

首先是声明:

#include<iostream>
#include<map>
using namespace std;
int main() {
	map<int, string> m; //空map
	map<int, string> m{{10,"www.kucoding.com"},{20,"www.kucoding.com"}}; //用初始值进行初始化
}

因为map是键值对,所以一个元素通过{}来表示,里面两个值分别为,多个键值对则用,分隔。

然后是添加:

	map<int, string> m; //空map
	m.insert(make_pair(10, "www.kucoding.com")); //1.通过函数生成pair

	pair<int, string> p(20, "www.kucoding.com");//2.通过pair直接构造
	m.insert(p);
	
	m.insert({30,"www.kucoding.com"}); //3.直接通过值默认构造出一个pair

	m[40] = "www.kucoding.com";//4.直接通过访问对应的键,不存在则创建,存在则修改

	m.emplace(50, "www.kucoding.com");  //5.通过传递参数构造一个pair

	auto beg = m.begin();
	m.emplace_hint(beg,60,"www.kucoding.com"); //6.在指定迭代器处插入数据

最简单的肯定是第四种了,它的功能就是不存在则创建,存在则修改:

m[40] = "www.kucoding.com";//4.直接通过访问对应的键,不存在则创建,存在则修改

但就插入值的效率而言,第3种insert更高,因为这种重载运算符的方法会先在内部构造一个带键但无值的pair,然后再将对应的值赋值给pair的值。

但如果是更新已有的数据,那么这种方法效率会更高,因为insert需要先构造一个pair完整对象,才能进行更新数据。

其次简单的便是第3种,但需要你有一定的理解,不然可能有点看不懂。

而第二种又比较繁琐,所以第一种用的就比较多,当然如果你能完全理解这种语句,那么使用第三种是完全没有问题的,而且还可以一次插入多个值。

注意一般带有emplace的函数效率都更高,这里同理。

接着就是访问:

	map<int, string> m{ {1,"kucoding1"},{2,"kucoding2"} }; //初始化2个键值对
	cout << m[1] << endl;
	cout << m.at(2) << endl;

	map<string, int> m1{ {"kucoding1",1},{"kucoding2",2} }; //初始化2个键值对
	cout << m1["kucoding1"] << endl;

访问一般就直接通过来访问即可。

我这里特意将键值顺序调换了一下,就是为了让你能够理解用键来访问值的含义,

at函数与前面一样,就是用来专门访问值的,如果不存在则抛出异常。

当然你可以获取整个键值对,而不是像上面只能获取:

	map<int, string> m{ {1,"csdn1"},{2,"csdn2"} }; //初始化2个键值对
	auto it = m.find(1); //查找该值对应的键值对,返回其迭代器
	cout << it->first<<endl;
	cout << it->second<<endl;

find函数通过键来查找,得到对应键值对的迭代器,你就可以用像指针一样的方式来访问该键值对的键与值,如果没有找到,那么它会返回结束迭代器,即end函数函数的值。

还有遍历:

	map<int, string> m{ {1,"csdn1"},{2,"csdn2"} }; //初始化2个键值对

	for (auto i = m.begin(); i !=m.end(); i++) //通过迭代器遍历
	{
		cout << i->first << endl;
		cout << i->second << endl;
	}

	for (auto& i : m) { //高级遍历
		cout << i.first;
		cout << i.second;
	}
作者:余识
全部文章:0
会员文章:0
总阅读量:0
c/c++pythonrustJavaScriptwindowslinux