STL-关联容器

关联容器

使用关联式容器存储的元素,都是一个一个的“键值对”( <key,value> ),底层选用了 「红黑树」这种数据结构来组织和存储各个键值对。
除此之外,序列式容器中存储的元素默认都是未经过排序的,而使用关联式容器存储的元素,默认会根据各元素的键值的大小做升序排序

1 pair 类模板

“键值对”算是什么数据类型?
C++ STL 标准库提供了 pair 类模板,其专门用来将 2 个普通元素 first 和 second创建成一个新元素<first, second>。

1.1 pair 的初始化
#include <utility>      
  • #1) 默认构造函数,即创建空的 pair 对象
    pair();
  • #2) 直接使用 2 个元素初始化成 pair 对象
    pair (const first_type& a, const second_type& b);
  • #3) 拷贝(复制)构造函数,即借助另一个 pair 对象,创建新的 pair 对象
  • template<class U, class V>
    pair (const pair<U,V>& pr);

在 C++ 11 标准中,在引入右值引用的基础上,pair 类模板中又增添了如下 2 个构造函数:

  • #4) 移动构造函数
    template<class U, class V> pair (pair<U,V>&& pr);
  • #5) 使用右值引用参数,创建 pair 对象
template<class U, class V> pair (U&& a, V&& b);
    // 无参构造
    pair <string, double> pair1;
    // 带初值的构造函数
    pair <string, string> pair2("2","2");  
    // 调用拷贝构造函数
    pair <string, string> pair3(pair2);
    //make_pair("4", "4")返回的是匿名临时变量的右值引用
    pair <string, string> pair4(make_pair("4", "4"));
    // string("5")返回的是匿名临时变量的右值引用
    pair <string, string> pair5(string("5"), string("5"));  
1.2 pair 之间的比较

<utility>头文件中除了提供创建 pair 对象的方法之外,还为 pair 对象重载了 <、<=、>、>=、==、!= 这 6 的运算符,其运算规则是:对于进行比较的 2 个 pair 对象,先比较 pair.first 元素的大小,如果相等则继续比较 pair.second 元素的大小。

2 map 容器

#include <map>

map 容器存储的都是 pair 对象,选用std::less排序规则(其中 T 表示键的数据类型),其会根据键的大小对所有键值对做升序排序。

使用 map 容器存储的各个键值对,键的值既不能重复也不能被修改。map 容器中存储的各个键值对不仅键的值独一无二,键的类型也会用 const 修饰,这意味着只要键值对被存储到 map 容器中,其键的值将不能再做任何修改

2.1 初始化
template < class Key,                                     // 指定键(key)的类型
           class T,                                       // 指定值(value)的类型
           class Compare = less<Key>,                     // 指定排序规则
           class Alloc = allocator<pair<const Key,T> >    // 指定分配器对象的类型

map 容器模板有 4 个参数,其中后 2 个参数都设有默认值。大多数场景中,我们只需要设定前 2 个参数的值,有些场景可能会用到第 3 个参数,但最后一个参数几乎不会用到。

std::map<std::string, int>myMap;空
std::map<std::string, int>myMap{ {"a",10},{"b",20} };包含有 2 个键值对
std::map<std::string, int>myMap{std::make_pair("a",10),std::make_pair("b",20)};
std::map<std::string, int>newMap(myMap);拷贝构造
std::map<std::string, int>newMap(disMap());移动构造,实参是右值引用
std::map<std::string, int>newMap(myMap.begin(), myMap.end());基于范围的拷贝构造
2.2 迭代器指向pair

map 容器配备的是双向迭代器(bidirectional iterator)。这意味着,map 容器迭代器只能进行 ++p、p++、–p、p–、*p 操作,并且迭代器之间只能使用 == 或者 != 运算符进行比较。

2.3 怎么获取pair->second?即键所对应的值
2.3.1 直接读取键值对的值
  • 使用[]运算符,既可读取(若找不到键值对会添加默认值),也可修改(覆盖),和增加;
  • 使用at()成员函数,只能读取,若找不到键值对,则抛出out_of_range 异常
std::map<string, int>myMap;空 map 容器

int value = myMap["a"];空map,肯定找不到,所以会添加默认值<"a",0>

myMap["b"] = 32;如果map中没有键"b",则会添加<"b",32>,若存在"b",则会修改

int bvalue = myMap.at("b");若找不到键"b"则会抛出异常
2.3.2 间接读取,借助迭代器来获取值

使用find()成员函数,找到则返回迭代器指向pair,找不到则返回指向end的迭代器

if((auto iter = myMap.find("a"))= myMap.end())如果不等于end说明找到了
    cout << iter->first << " " << iter->second << endl;
2.4 插入数据的方式

除了运算符[]可以插入数据,还可以使用insert()成员函数,有4种用法:

  • insert()不指定插入位置
std::pair<string, string> STL = { "a","123" };创建一个真实存在的键值对变量
auto ret = mymap.insert(STL);

auto ret = mymap.insert({ "b","456" });以右值引用的方式传递临时的键值对变量

返回值的含义:

	如果插入成功( key 无重复),其返回的 pair 对象(pair<iterator,bool>)中包含一个指向新插入key的迭代器和值为 1 的 bool 变量
	
	如果插入失败( key 重复了),会返回一个 pair 对象(pair<iterator,bool>),其中包含一个指向已存在的 key 的迭代器和值为 0 的 bool 变量。
  • insert(),并且指定插入位置
std::pair<string, string> STL = { "a","123" };
std::map<string, string>::iterator it = mymap.begin();
auto iter1 = mymap.insert(it, STL);向 it 位置以普通引用的方式插入 STL

std::map<string, string>::iterator it = mymap.begin();
auto iter2 = mymap.insert({ "b","456" });

返回值的含义:

	如果插入成功( key 无重复),insert() 方法会返回一个指向 map 容器中已插入键值对的迭代器;
	
	如果插入失败( key 重复了),insert() 方法同样会返回一个迭代器,该迭代器指向 map 容器中和 val 具有相同键的那个键值对。
  • 一次性插入多个pair对象
template <class InputIterator> void insert (InputIterator first, InputIterator last);基于map范围的插入

void insert ({val1, val2, ...});列举多个pair的插入
  • 比insert()效率更好的emplace()
    实现相同的插入操作,无论是用 emplace() 还是 emplace_hont(),都比 insert() 方法的效率高
auto ret = myMap.emplace("a", "123");
返回值也是一个 pair 对象,其中 pair.first 为一个迭代器(指向新插入的pair或已重复的pair),pair.second 为一个 bool 类型变量
iter = myMap.emplace_hint(myMap.begin(), "b", "456");
返回迭代器,指向新插入的pair或已重复的pair
2.5 删除数据
erase();传入迭代器可以删除指定pair,也可以基于范围删除pair,返回删除的个数
clear();可以一次性删除所有

3 multimap 容器

#include <map>

multimap 容器具有和 map 相同的特性:会对 key 排序
区别在于:
multimap 容器中可以同时存储多(≥2)个键相同的键值对。

Q:同名KEY怎么索引?
A:只能通过迭代器!不能用 at() 成员方法,也没有重载 [] 运算符。
因为 multimap 容器中指定的键可能对应多个键值对,而不再是 1 个。

3.1 初始化
- std::multimap<std::string, std::string>mymultimap;
带参数初始化
- multimap<string, string>mymultimap{ {"a", "1"},{"b", "2"},{"c", "3"} };
- multimap<string, string>mymultimap{ pair<string,string>{"a", "1"},pair<string,string>{"b", "2"},pair<string,string>{"c", "3"} };
- multimap<string, string>mymultimap{ make_pair("a", "1"),make_pair("b", "2"),make_pair("c", "3") };
拷贝构造
- multimap<string, string>newmultimap(mymultimap);
右值引用构造
- multimap<string, string>newmultimap(dismultimap());
基于范围的拷贝构造
- multimap<string, string>newmultimap(mymultimap.begin(), mymultimap.end());
3.2 读取、遍历操作

multimap支持多个1个key对应多个value,它内部的结构类似于:

{{1,a},{1,b},{1,c},{2,d},{3,e},{3,f}}

这些同key键值对是并列存在的,对于key=1的pair有多少个,使用count(key)可以得到,利用它来作为迭代器的偏移量。

  • count(key)
    读取或遍历multimap时需要配合成员函数,查找键为 key 的键值对的个数并返回。
  • lower_bound(key)
    返回一个指向当前 multimap 容器中第一个大于或等于 key 的键值对的双向迭代器。
  • upper_bound(key)
    返回一个指向当前 multimap 容器中第一个大于 key 的键值对的迭代器。
  • equal_range(key)
    该方法返回一个 pair 对象(包含 2 个双向迭代器),其中 pair.first 和 lower_bound() 方法的返回值等价,pair.second 和 upper_bound() 方法的返回值等价。也就是说,该方法将返回一个范围,该范围中包含的键为 key 的键值对。
3.3 删除、插入、修改

multimap与map的方法一样,需要用到迭代器

4 set 容器

#include <set>

set译名为集合,表明了没有重复的内容。例如集合{a,b,c},包含了3个元素。我们常常使用set来去重~

小知识:从set的实现角度来看,set的元素也是pair类型的,只不过key与value是相等的而已。
4.1 初始化
std::set<std::string> myset;
std::set<std::string> myset{1,2,3};
std::set<std::string> copyset(myset);
std::set<std::string> copyset(retSet());
std::set<std::string> copyset(myset.begin(), myset.end());
4.2 插入元素
  • insert()不指定插入位置
string str = "abc";
auto retpair = myset.insert(str);普通引用传值

auto retpair = myset.insert("123");右值引用传值

返回值的含义:

	如果插入成功( key 无重复),其返回的 pair 对象中包含一个指向新插入key的迭代器和值为 1 的 bool 变量。
	
	如果插入失败( key 重复了),会返回一个 pair 对象(pair<iterator,bool>),其中包含一个指向 key 的迭代器和值为 0 的 bool 变量。
  • insert()指定了插入位置
auto iter = myset.insert(myset.begin(),str);普通引用传值
auto iter = myset.insert(myset.end(),"123");右值引用传值

返回值的含义:

	如果插入成功( key 无重复),insert() 方法会返回一个指向 set 容器中已插入元素的迭代器;
	
	如果插入失败( key 重复了),insert() 方法同样会返回一个迭代器,该迭代器指向 set 容器中和 str 具有相同键的那个元素。
  • 一次性插入多个pair对象
void insert(InputIterator first, InputIterator last);插入其它 set 容器指定区域内的所有元素
void insert ({val1, val2, ...});列举多个元素的插入
  • 用性能更好的emplace()取代insert()
    auto ret = myset.emplace("124");
    该方法的返回值类型为 pair 类型,其包含 2 个元素,一个迭代器和一个 bool 值,迭代器指向新添加元素(或已存在的旧元素)

    set<string>::iterator iter = myset.emplace_hint(myset.begin(), "123");
    该方法需要额外传入一个迭代器,用来指明新元素添加到 set 容器的具体位置(新元素会添加到该迭代器指向元素的前面);

    返回值是一个迭代器,而不再是 pair 对象。当成功添加元素时,返回的迭代器指向新添加的元素;反之,如果添加失败,则迭代器就指向 set 容器和要添加元素的值相同的元素。

4.3 删除数据

set没有修改元素的功能,一般是先删除,再添加。

//删除 set 容器中值为 val 的元素,返回成功删除的个数
size_type erase (const value_type& val);
//删除 position 迭代器指向的元素,返回后续元素的迭代器
iterator  erase (const_iterator position);
//删除 [first,last) 区间内的所有元素,返回后续元素的迭代器
iterator  erase (const_iterator first, const_iterator last);
//删除全部
void clear();

5 multiset 容器

  • 与set的共同点在于:
    元素只需要给定value即可,
    默认做升序排序,
    元素不可以修改(会破坏结构)若要修改一般是先删除再插入。
  • 和 set 容器不同的是:
    multiset 容器可以存储多个value相同的元素,那么它的查找、遍历、删除方式都要依赖count(value),这一点与multimap相同。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值