关联容器

[0. 概述]

关联容器和顺序容器的本质差别在于:

关联容器通过键(key)存储和读取元素,而顺序容器则通过元素在容器中的位置顺序存储和访问元素。

虽然关联容器的大部分行为与顺序容器相同,但其独特之处在于支持键的使用。
关联容器(Associative containers)支持通过键来高效地查找和读取元素。

两个基本的关联容器类型是 map 和 set。
map 的元素以键-值(key-value)对的形式组织:
key 用作元素在 map 中的索引,而 value 则表示所存储和读取的数据。
set 仅包含一个键,并有效地支持关于某个键是否存在的查询。

一般来说,如果希望有效地存储不同值的集合,那么使用 set 容器比较合适,
而 map 容器则更适用于需要存储(乃至修改)每个键所关联的值的情况。
在做某种文本处理时,可使用 set 保存要忽略的单词。
而字典则是 map 的一种很好的应用:单词本身是键,而它的解释说明则是值。
set 和 map 类型的对象所包含的元素都具有不同的键,不允许为同一个键添加第二个元素。
如果一个键必须对应多个实例,则需使用 multimap 或 multi set,这两种类型允许多个元素拥有相同的键。

关联容器支持很多顺序容器也提供的相同操作,此外,还提供管理或使用键的特殊操作。

// 表 10.1. 关联容器类型
map       关联数组:元素通过键来存储和读取

set       大小可变的集合,支持通过键实现的快速读取

multimap    支持同一个键多次出现的 map 类型

multiset    支持同一个键多次出现的 set 类型

[1. 引言:pair 类型]

在开始介绍关联容器之前,必须先了解一种与之相关的简单的标准库类型:
pair(表 10.2),该类型在 utility 头文件中定义。

// 表 10.2 pairs 类型提供的操作
pair<T1, T2> p1;         创建一个空的 pair 对象,它的两个元素分别是 T1 和 T2 类型。

pair<T1, T2> p1(v1, v2);    创建一个 pair 对象,它的两个元素分别是 T1 和 T2 ,
                  其中 first 成员初始化为 v1,而 second 成员初始化为 v2

make_pair(v1, v2);          以 v1 和 v2 值创建一个新 pair 对象,其元素类型分别是 v1 和 v2 的类型

p1 < p2                     两个 pair 对象之间的小于运算,其定义遵循字典次序:
                如果 p1.first < p2.first 
                或 !(p2.first < p1.first) && p1.second < p2.second,则返回 true

p1 == p2             如果两个 pair 对象的 first 和 second 成员依次相等,则这两个对象相等。
                该运算使用其元素的 == 操作符

p.first              返回 p 中名为 first 的(公有)数据成员

p.second              返回 p 的名为 second 的(公有)数据成员

1.1 pair 的创建和初始化

pair 包含两个数据值。与容器一样,pair 也是一种模板类型。
z注意,在创建 pair 对象时,必须提供两个类型名:
pair 对象所包含的两个数据成员各自对应的类型名字,这两个类型不必相同。

pair<string, string> anon; // holds two strings
pair<string, int> word_count; // holds a string and an int
pair<string, vector<int> > line; // holds string and vector<int>

如果在创建 pair 对象时不提供初始化式,则调用默认构造函数对其成员采用值初始化。
当然,也可在定义时为每个成员提供初始化式:

pair<string, string> author("James", "Joyce");

如果需要定义多个相同的 pair 类型对象,可考虑利用 typedef 简化其声明:

typedef pair<string, string> Author;
Author proust("Marcel", "Proust");
Author joyce("James", "Joyce");

1.2 pairs 对象的操作

与其他标准库类型不同,对于 pair 类,可以直接访问其数据成员:
其成员都是 public 的,分别命名为 first 和 second。
只需使用普通的点操作符即可:

string firstBook;
// access and test the data members of the pair
if (author.first == "James" && author.second == "Joyce")
firstBook = "Stephen Hero";

标准库只为 pair 类型定义了表 10.2 所列出的数量有限的操作。

1.3 生成新的 pair 对象

除了构造函数,标准库还定义了一个 make_pair 函数,
由传递给它的两个实参生成一个新的 pair 对象。
可使用如下函数创建新的 pair 对象,并赋给已存在的 pair 对象:

pair<string, string> next_auth;
string first, last;
while (cin >> first >> last) {
  // generate a pair from first and last
  next_auth = make_pair(first, last);
  // process next_auth...
}

在 while 循环条件中读入的作者名字作为实参,
调用 make_pair 函数生成一个新的 pair 对象。
此操作等价于下面更复杂的操作:

// use pair constructor to make first and last into a pair
next_auth = pair<string, string>(first, last);

由于 pair 的数据成员是公有的,因而可如下直接地读取输入:

pair<string, string> next_auth;
// read directly into the members of next_auth
while (cin >> next_auth.first >> next_auth.second) {
  // process next_auth...
}


[2. 关联容器]
关联容器共享大部分的顺序容器操作。但是,关联容器不提供 ——
front、 push_front、 pop_front、back、push_back 以及 pop_back 操作。

顺序容器和关联容器公共的操作包括下面的几种:

1. 构造函数:
  C<T> c; // creates an empty container
  // c2 must be same type as c1
  C<T> c1(c2); // copies elements from c2 into c1
  // b and e are iterators denoting a sequence
  C<T> c(b, e); // copies elements from the sequence into c
  "注意 "—— 关联容器不能通过容器大小来定义,因为这样的话就无法知道键所对应的值是什么。

2. 关系运算。

3. begin、end 操作。
  c.begin(); c.end(); c.rbegin(); c.rend()。

4. 类型别名 typedef :
  (size_type, iterator, const_iterator,reverse_iterator, const_reverse_iterator, 
  difference_type, value_type, reference, const_referenc )
  注意,对于 map 容器,value_type 并非元素的类型,而是描述键及其关联值类型的 pair 类型。
  以后将详细解释 map 中的类型别名。

5. 赋值操作。
  c1 = c2; c1.swap(c2);

6. clear 和 erase 操作。
  c.erase(p); c.erase(b, e); c.clear();

7. 容器大小的操作。
  c.size(); c.max_size(); c.empty();

8. 根据键排列元素

除了上述列出的操作之外,关联容器还提供了其他的操作。

对于顺序容器也提供的相同操作,关联容器也重新定义了这些操作的含义或返回类型,
其中的差别在于关联容器中使用了键 ——
" 关联容器的元素根据键的次序排列" 这一事实就是一个重要的结论:
在迭代遍历关联容器时,我们可确保按键的顺序的访问元素,而与元素在容器中的存放位置完全无关。

原文地址:https://www.cnblogs.com/yshl-dragon/p/3140592.html