目录
一、set
1、set的用法
2、multiset
二、map
1、map的用法
2、map的operator[]
3、multimap
结语
前言:
C++中的map和set容器属于关联式容器,与序列式容器不同的地方在于(序列式容器即vector、list,其底层是由线性数据结构实现的容器),关联式容器存储的是键值对,键值对是一个结构体,他只有两个成员变量:key,value,并且key和value是一一对应的,key表示键值,而value就是key对应的数据。
关联式容器的底层由两种结构实现:树型结构(红黑树)、哈希结构。树型结构实现的容器有:map、multimap、set、multiset,哈希结构实现的容器有:unordered_map和unordered_set,本文着重介绍树型结构实现的容器。正是因为关联式容器在底层的结构上采用比序列式容器更为复杂的结构,所以关联式容器在查找数据时效率较序列式容器更高。
一、set
template < class T,
class Compare = less<T>,
class Alloc = allocator<T>
> class set;
1、set的用法
set只有唯一标识数据,即:value,但是这个value属于键值,也就是前面讲到的key,简单来说就是set在底层也是以键值对存放的,只不过键值对中不是<key,value>,而是<value,value>。并且通过value的值来确定其存放的顺序。
set作为STL的容器之一,自然是可以实现数据的存储,只不过一般容器可以实现增删查改的功能,而set只能实现增删查的功能,原因就在于set的底层是红黑树(搜索二叉树),存放在set中的数据实际上存储在红黑树的节点中,红黑树的节点都是按照严格的规则进行摆放的,若修改一个数则会导致大消耗的调整,因此set不允许修改数据。但也正是红黑树,所以在迭代数据时,可以按照大小来打印数据(中序遍历树结构)。
set的测试代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include<set>
#include<iostream>
using namespace std;
int main()
{
set<int> s1;
//插入大量重复数据
s1.insert(3);
s1.insert(1);
s1.insert(4);
s1.insert(1);
s1.insert(2);
s1.insert(1);
s1.insert(3);
set<int>::iterator it1 = s1.begin();
while (it1 != s1.end())//迭代器遍历
{
cout << *it1 << " ";
++it1;
}
cout << endl;
s1.erase(3);//删除3
// 打印删除后数据
for (auto e : s1)
{
cout << e << " ";
}
cout << endl;
return 0;
}
运行结果:
从结果可以看到,set还具备去重的功能,并且打印出来的数据是具有顺序的,这些set的自带功能在特殊的场景下用起来是非常方便的。
但是有一个问题,对象s1的类型是int,虽然底层存放的是键值对<value,value>,但是到了用户使用的应用层做了优化,只需要显示传递一个value类型即可,否则set的模板参数显示传递就要写<int,int>了,过于麻烦。
2、multiset
multiset与set的区别在于:multiset允许存储重复的值。并且删除的值如果是重复的,那么该值都会被删除。
multiset测试代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include<set>
#include<iostream>
using namespace std;
int main()
{
multiset<int> s1;
//插入大量重复数据
s1.insert(3);
s1.insert(1);
s1.insert(4);
s1.insert(1);
s1.insert(2);
s1.insert(1);
s1.insert(3);
set<int>::iterator it1 = s1.begin();
while (it1 != s1.end())//迭代器遍历
{
cout << *it1 << " ";
++it1;
}
cout << endl;
s1.erase(3);//删除3
// 打印删除后数据
for (auto e : s1)
{
cout << e << " ";
}
cout << endl;
return 0;
}
运行结果:
二、map
template < class Key,
class T,
class Compare = less<Key>,
class Alloc = allocator<pair<const Key,T> >
> class map;
1、map的用法
map与set的区别在于,map的键值对是<key,value>,通常用pair结构体来表示这个键值对。以key的值来确定每个pair类型元素的顺序序列并且key所在的元素必须唯一(即去重),value的值是与key相关联的数据。
pair结构体的结构如下:
template <class T1, class T2>
struct pair
{
typedef T1 first_type;
typedef T2 second_type;
T1 first;
T2 second;
pair(): first(T1()), second(T2())//默认构造
{}
pair(const T1& a, const T2& b): first(a), second(b)//带参数的构造函数
{}
};
map测试代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<map>
using namespace std;
int main()
{
map<string, string> dict;
dict.insert(pair<string, string>("sort", "排序"));//检查去重
dict.insert(make_pair("sort", "排序"));
dict.insert(make_pair("string", "字符串"));
dict.insert(make_pair("count", "计数"));
auto dit = dict.begin();
while (dit != dict.end())
{
//先对迭代器dit解引用拿到pair结构体,然后在解引用拿到pair中的成员first和second
cout << (*dit).first << ":" << (*dit).second << endl;
++dit;
}
cout << endl;
return 0;
}
运行结果:
从结果可以看到,map的插入函数需要手动构建一个pair作为该函数的实参,而这里用到的make_pair是一个函数模板,该函数会返回一个pair对象,这样就能够达到inset函数的要求了。
具体示意图如下:
2、map的operator[]
map的operator[]运算符重载用法很经典,我们对[]的认识是,他可以查看数组中某一位置的值,并且可以修改这个元素,而map的operator[]在以上功能的前提下还具备插入功能,只不过对map的[]修改是更改value的内容,而不更改key的内容,同理通过[]对map的查看是查看元素中value的值。
以下map的operator[]的实现示意图可以更好的解释其功能:
ret是一个对象,他用来接收Insert的返回值,他跟Insert的make_pair返回的临时对象是两个对象,ret对象中里面有一个迭代器,该迭代器指向通过Insert函数插入到map中pair元素。operator[]的返回值实际上就是通过ret找到插入元素的第二个成员变量value,并且返回他的引用,所以[]可以更改map中元素的value数据。
测试operator[]的代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<map>
using namespace std;
int main()
{
map<string, string> dict;
dict.insert(make_pair("sort", "排序"));
dict.insert(make_pair("string", "字符串"));
dict.insert(make_pair("count", "计数"));
dict["ZH"]; // 成功插入,但是value值没有显示给,因此value调用string的默认构造--即nullptr
dict["OBJ"] = "后缀"; // 插入+修改,插入的元素key为OBJ,value从nullptr修改成"后缀"
dict["sort"] = "(排序)"; // 修改,将key:sort从原来的排序修改为“排序”
cout << dict["sort"] << endl; // 查找,打印sort的value
cout << endl;
auto dit = dict.begin();
while (dit != dict.end())
{
//cout << (*dit).first << ":" << (*dit).second << endl;
cout << dit->first << ":" << dit->second << endl;
++dit;
}
cout << endl;
return 0;
}
运行结果:
注意:所以map插入和修改都是可以直接用【】,但是【】的查找功能用起来会有弊端,如果要查找的元素不在map里面就会自动插入一个该元素,失去了单纯查找的本意。
3、multimap
multimap与map的区别在于:前者允许key值冗余,即不会对数据起到去重的作用。
multimap测试代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<map>
using namespace std;
int main()
{
multimap<string, string> dict;
dict.insert(make_pair("sort", "排序"));
dict.insert(make_pair("sort", "排序"));
dict.insert(make_pair("string", "字符串"));
dict.insert(make_pair("string", "字符串"));
dict.insert(make_pair("count", "计数"));
dict.insert(make_pair("count", "计数"));
auto dit = dict.begin();
while (dit != dict.end())
{
//cout << (*dit).first << ":" << (*dit).second << endl;
cout << dit->first << ":" << dit->second << endl;
++dit;
}
cout << endl;
return 0;
}
运行结果:
结语
以上就是关于map和set以及multimap、multiset的讲解,实际中map和set还是用的最多,他们的区别无非在于set只有一个value作为key键值而map中即有key键值也有与其对应的value键值,也正是因为set没有value键值,所以set没有实现‘[]'的运算符重载,而map实现了‘[]'的运算符重载。
最后希望本文可以给你带来更多的收获,如果本文对你起到了帮助,希望可以动动小指头帮忙点赞👍+关注😎+收藏👌!如果有遗漏或者有误的地方欢迎大家在评论区补充,谢谢大家!!