关联容器
概述
关联容器支持普通容器的操作,不支持顺序容器中位置相关的操作,如push_back等,也不支持构造函数或插入操作的接收一个元素值和数量值得操作,如vector< int>v(10,0)。
普通容器的操作 | |
---|---|
类型别名 | |
iterator | 此容器类型的迭代器 |
const_iterator | 可以读取元素,但不能修改元素的迭代器类型 |
size_type | 无符号整数类型,足够保存此种容器类型最大可能容器的大小 |
diference_type | 带符号整数类型,足够保存两个迭代器之间的距离 |
value_type | 元素类型 |
refernce | 左值引用;与value_type&含义相同 |
const_refernce | 元素的const左值类型(const value_type&) |
构造函数 | |
C c; | 默认构造函数,构造空容器(array除外) |
C c1(c2) | 用c2来拷贝构造c1 |
C c3(b, e) | 用迭代器b,e范围内的元素拷贝构造c3(array不支持) |
C c{a,b,d,…} | 列表初始化c |
赋值与交换 | |
c1 = c2 | 将c1中的值替换成c2的值 |
c1 = {a,b,d,…} | 将c1中的值替换成列表元素 |
a.swap(b) | 交换a和b的元素 |
swap(a,b) | 与上等价 |
大小 | |
c.size() | c中元素的数目(forward_list不支持) |
c.max_size() | c中可保存的最大元素数目 |
c.empty() | 若c中存储了元素,则返回false,否则返回true |
添加删除元素(array不支持,不同容器接口不尽相同) | |
c.insert(args) | 将args插入c |
c.emplace(inits) | 使用inits构造c中的一个元素 |
c.erase(args) | 删除args指定的元素 |
c.clear() | 删除c中所有元素,返回void |
关系运算符 | |
==, != | 所有容器都支持相等(不等)运算符 |
<,<=,>,>= | 关系运算符(无序关联容器不支持) |
获取迭代器 | |
c.begin(),c.end() | 返回指向c中首元素和尾元素之后位置的迭代器 |
c.cbegin(),c.cend() | 返回常迭代器 |
反向容器的额外成员 | |
reverse_iterator | 逆序寻址元素的迭代器 |
const_reverse_iterator | 不修改元素的逆序迭代器 |
c.rbegin(),c.rend() | 返回指向c的尾元素和首元素之前位置的迭代器 |
c.crbegin(),c.crend() | 返回const_reverse_iterator |
关联容器的迭代器都是双向的。
关联容器额外的类型别名 | |
---|---|
key_type | 此容器类型的关键字类型 |
mapped_type | 每个关键字关联的类型,只适用于map |
value_type | 对于set,与key_value相同,对于map,为pair<const key_type, mapped_type> |
关联容器通常不使用泛型算法,也就是STL中的算法。因为set中元素是const的,map中pair.first也是const的,会修改容器元素的算法都不能使用。可以用于只读取元素的算法,但是会比较慢,不如关联容器内置的算法。
有序容器
pair
存放在头文件utility中。
pair操作 | |
---|---|
pair<T1, T2>p; | 定义类型为T1、T2类型的pair,并进行值初始化。 |
pair<T1, T2>p(v1,v2); | 如上,其first和second成员被初始化为v1,v2。 |
pair<T1,T2>p = {v1,v2}; | 等价于上面。 |
make_pair(v1,v2); | 返回v1,v2初始化的pair,其类型从v1,v2类型推断出来。 |
p.first | 返回p的名为first的共有数据成员。 |
p.second | 返回p的名为second的共有数据成员。 |
p1 relop p2 | 关系运算符(<,<=,>,>=)按字典序定义:当p1.first<p2.first && p1.second < p2.second成立时,p1 < p2返回true。 |
p1 == p2 | first和second两个成员分别相等时成立。 |
map(multimap)的value_type是一个pair,其first成员保存const的关键字,second成员保存值。
遍历map
map<string, int>word_count = {{"Tim", 20}, {"Jack", 15}, {"Rose", 30}};
auto map_iter = word_count.cbegin();
while(map_iter != word_count.cend()){
cout << map_iter->first << endl;
cout << map_iter->second << endl;
++map_iter;
}
map、multimap、set、multiset的迭代器按照关键字升序遍历元素。
添加元素:
map | |
---|---|
map.insert(v); | v是value_type对象 |
map.emplace(args); | 返回pair,其first是一个迭代器,指向具有指定关键字的元素,second是指示插入是否成功的bool值,对于multimap和multiset总会插入给定元素,并返回指向新元素的迭代器。 |
map.insert(b, e); | b,e是迭代器,表示一个c::value_type类型的范围。 |
map.insert({…}) | 插入花括号的值列表,返回void。对于set和map,只会插入不在容器中的关键字,对于multiset和multimap则每个元素都会插入。 |
map.insert(p,v) | p是迭代器,表示从p开始搜索新元素应该存储的位置,返回一个指向具有给定关键字的迭代器。 |
map.emplace(p,v) | 同上版本 |
对于插入的返回值,可以写成这样:
pair<map<string, int>::iterator, bool> ret = word_count.insert(make_pair(word, 1));
//插入的返回值是一个pair,其first是word_count同类型的迭代器,,second是表示插入是否成功的bool值。
set的迭代器是const的,也就是说迭代器只能访问set成员,而不能修改。
向multimap、multiset中插入元素:
multimap<string, string>authors;
//插入 第一个元素,关键字为Tom
authors.insert({"Tom", "Book1"});
//插入第二个元素,关键字还是Tom
authors.insert({"Tom", "Book2"});//正确,multi关联容器关键字不唯一。
删除元素
关联容器定义了3个版本的erase:
关联容器删除元素 | |
---|---|
c.erase(k) | 删除c中每个关键字为k的元素。返回size_type值,表示删除元素的数量。 |
c.erase§ | 从c中删除迭代器p指向的c中的元素,且不能是c.end()。返回指向p之后元素的迭代器。 |
c.erase(b, e) | 删除迭代器b和e所表示范围内的元素,返回迭代器e。 |
map的下标操作(查找元素时推荐用find函数):
map和unordered_map支持下表运算符和对应的at()函数。set类、multi类不支持。
map\unordered_map的下标操作和at()函数 | |
---|---|
c[k] | 返回关键字为k的元素,若c中不存在k,则添加一个关键字为k的元素并进行值初始化。 |
c.at(k) | 访问关键字为k的元素,带参数检查,若k不在c中,抛出out_of_range异常 |
下标操作要注意的是,如果下标关键字不在map中,会为该容器自动创建一个该关键字的元素,且其映射值会进行值初始化!
map<string, int>word_count; //这是空的map
word_count["Tom"] = 1; //map中没有关键字为Tom的元素,自动创建该元素。。
并且由于下标操作会改变容器,所以只能对非const的map和unordered_map进行下标操作!
map下标运算符返回值与其他下标运算符返回值另一个不同之处:同常情况下,解引用一个迭代器返回的类型和下标运算返回的类型是一样的,但是对map进行下标操作是,返回的是mapped_type对象,解引用map的迭代器时会得到value_type对象。
map的下标运算符与其他下标运算符一样,返回一个左值,我们可以对下标操作的元素进行读和写。
cout<< word_count["Tom"]; //提取关键字为Tom的元素,打印结果是1.
++ word_count["Tom"]; //修改元素
cout<< word_count["Tom"] //会输出2
访问元素
访问关联容器中元素的操作 | |
---|---|
下标操作和at()只适用于非const的map和unordered_map。 | |
c.find(k) | 返回指向第一个关键字为k的迭代器,若k不在容器中,则返回尾后迭代器。 |
c.count(k) | 返回关键字等于k的元素的数量。对于不允许重复关键字的容器,返回值只能是0或1. |
c.lower_bound(k) | 返回指向第一个关键字不小于k的元素的迭代器。 |
c.upper_bound(k) | 返回指向第一个关键字大于k的元素的迭代器。 |
c.equal_range(k) | 返回一个迭代器pair,表示关键字等于k的元素的范围,若k不在c中,则pair的两个成员均为c.end()。 |
如果只需要确定元素是否在一个容器中,使用find(k)而不是[k]!
对于multimap和multiset,如果具有多个相同的关键字,则这些元素会相邻存储。
对于c.lower_bound()和c.upper_bound(),两者组合可得到关键字等于k的元素的范围。
for (auto begin = word_count.lower_bound(k), end = word_count.upper_bound(k); begin != end; ++begin){
cout << begin.second <<endl; //输出关键字等于k的元素的值
}
如果lower_bound和upper_bound返回相同迭代器,说明该关键字不在容器中。
equal_range(k)
直接返回关键字等于k的元素的范围,等价于lower_bound和upper_bound。其返回值是一个迭代器pair。第一个迭代器指向第一个与关键字匹配的元素,第二个迭代器指向最后一个匹配的元素的后一个位置。
无序容器
unordered_map、unordered_set、unordered_multimap、unordered_multiset。
无序关联容器不是使用比较运算符(<,<=,>,>=,默认使用<来进行比较)来组织元素的,而是使用哈希函数和关键字类型的==运算符。
无序容器在存储上组织为一个桶,使用哈希函数将元素映射到相应桶。对哈希函数的要求是,相同的参数,哈希函数必须总是产生相同都得结果,但是不同参数产生相同的结果也是允许的。
与有序容器不同,不能直接定义关键字类型为自定义类型的无序容器,如果确实需要定义这样类型的容器,需要提供该自定义类型的hash模板。
无序容器管理操作 | |
---|---|
桶接口 | |
c.bucket_count() | 正在使用的桶的数目。 |
c.max_bucket_count() | 容器能容纳最多的桶的数量。 |
c.bucket_size(n) | 第n个桶有多少个元素。 |
c.bucket(k) | 关键字为k的元素在哪个桶中。 |
桶迭代 | |
local_iterator | 可以用来访问桶中元素的迭代器类型。 |
const_local_iterator | 桶迭代器的const版本。 |
c.begin(n),c.end(n) | 桶n的首元素迭代器和尾后迭代器。 |
c.cbegin(n),c.cend(n) | 上面的const版本。 |
哈希策略 | |
c.load_factor() | 每个桶的平均元素数量,float型。 |
c.max_load_factor() | c试图维护的平均桶大小,返回float值。c会在需要时添加新的桶,使得load_factor<=max_load_factor。 |
c.rehsah(n) | 重组存储,使得bucket_count >= n且bucket_count>=n。 |
c.reserve(n) | 重组存储,使得c可以保存n个元素且不必rehash。 |