第十一章 关联容器
- 关联容器基于关键字访问元素,顺序容器基于位置访问元素
- 关联容器类型
11.2 关联容器概述
- 关联容器的初始化可以使用直接初始化(圆括号初始化)、列表初始化、拷贝初始化、迭代器范围初始化(会对关键字自动去重)
map
类型通常被称为关联数组- 对于有序容器,关键字类型必须定义元素比较的方法(即
<
),严格弱序 - pair类型
- 定义在
utility
头文件 - map中的每个元素都是一个pair类型的对象,pair是一个模板类型,保存两个名为first和second的共有数据成员,first保存关键字,second保存值
- 定义在
操作 | 解释 |
---|---|
pair<T1, T2> p; | p 是一个pair ,两个类型分别是T1 和T2 的成员都进行了值初始化。 |
pair<T1, T2> p(v1, v2); | first 和second 分别用v1 和v2 进行初始化。 |
pair<T1, T2>p = {v1, v2}; | 等价于`p(v1, v2) |
make_pair(v1, v2); | pair 的类型从v1 和v2 的类型推断出来。 |
p.first | 返回p 的名为first 的数据成员。 |
p.second | 返回p 的名为second 的数据成员。 |
p1 relop p2 | relop(relational operations,<,>,<=,>=),运算关系符按字典序定义。 |
p1 == p2 | 必须两对元素两两相等 |
p1 != p2 | 同上 |
|
|
11.3 关联容器操作
- 关联容器额外的类型别名:
类型别名 | 解释 |
---|---|
key_type | 此容器类型的关键字类型 |
mapped_type | 每个关键字关联的类型,只适用于map 系列 |
value_type | 对于map ,是pair<const key_type, mapped_type> (注意关键字部分有const); 对于set ,和key_type 相同。 |
- 解引用一个关联容器迭代器时,会得到一个类型为容器的
value_type
的值的引用。map
而言,value_type是pair类型(pair<const key_type, mapped_type>
)set
而言,value_type是const key_type
,普通迭代器和const迭代器都是只读的,不能修改值
添加元素:insert
insert 操作 | 关联容器 |
---|---|
c.insert(v) c.emplace(args) | v 是value_type 类型的对象;args 用来构造一个元素。函数返回一个pair ,指向具有指定关键字的元素的迭代器,和一个指示插入是否成功的bool 值。 |
c.insert(b, e) c.insert(il) | b 和e 是迭代器,表示一个c::value_type 类型值的范围;il 是这种值的花括号列表。函数返回void 。对于 map 和set ,只插入关键字不在c 中的元素。 |
c.insert(p, v) c.emplace(p, args) | 类似insert(v) ,但将迭代器p 作为一个提示,指出从哪里开始搜索新元素应该存储的位置。返回一个迭代器,指向具有给定关键字的元素。 |
- 向
map
添加元素(在参数列表中构建pair),例子:1 2 3 4
word_count.insert({word, 1}); word_count.insert(make_pair(word, 1)); word_count.insert(pair<string, size_t>(word, 1)); word_count.insert(map<string, size_t>::value_type (word, 1));
- 相对于下标操作,多使用insert(因为有返回值)
- 例子:wordcount
1 2 3 4
map<string, size_t> word_count; string word; while(cin >> word) ++word_count.insert({word, 0}).first->second;
- 例子:wordcount
删除元素:erase
操作 | 解释 |
---|---|
c.erase(k) | 从c 中删除每个关键字为k 的元素。返回一个size_type 值,指出删除的元素的数量。(顺序容器没有该操作) |
c.erase(p) | 从c 中删除迭代器p 指定的元素。p 必须指向c 中一个真实元素,不能等于c.end() 。返回一个指向p 之后元素的迭代器。 |
c.erase(b, e) | 删除迭代器对b 和e 所表示范围中的元素。返回e 。 |
- 注意遍历容器删除元素时,map与vector不同
|
|
下标操作
操作 | 解释 |
---|---|
c[k] | 返回关键字为k 的元素;如果k 不在c 中,添加一个关键字为k 的元素,对其值初始化。 |
c.at(k) | 访问关键字为k 的元素,带参数检查;若k 不存在在c 中,抛出一个out_of_range 异常。 |
- 下标和
at
操作只适用于非const
的map
和unordered_map
,即使访问操作也不行 - map的下标操作只能返回非常量引用,如果map本身是常量,则无法使用下标访问元素,只能使用at函数
查找元素
在一个关联容器中查找元素:
操作 | 解释 |
---|---|
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 不存在,pair 的两个成员均等于c.end() 。 |
lower_bound
和upper_bound
不适用于无序容器。- 如果
multimap
或multiset
中有多个元素有相同关键字,则这些元素在容器中会相邻存储
11.4 无序容器
- 有序容器使用比较运算符来组织元素;无序容器使用哈希函数和关键字类型的
==
运算符。 - 无序容器在存储上组织为一组桶(bucket),每个桶保存零个或多个元素。无序容器使用一个哈希函数将元素映射到桶。无序容器首先计算元素的哈希值,找到应该搜索哪个桶(相同哈希值的元素保存到相同的桶中),再搜索桶。 无序容器管理操作:
操作 | 解释 |
---|---|
桶接口 | |
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_local_iterator 。 |
哈希策略 | |
c.load_factor() | 每个桶的平均元素数量,返回float 值。 |
c.max_load_factor() | c 试图维护的平均比桶大小,返回float 值。c 会在需要时添加新的桶,以使得load_factor<=max_load_factor |
c.rehash(n) | 重组存储,使得bucket_count>=n ,且bucket_count>size/max_load_factor |
c.reverse(n) | 重组存储,使得c 可以保存n 个元素且不必rehash 。 |