关联容器和顺序容器有着很大不同,关联容器中的元素是按照关键字来保存和访问的,而顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的。两个主要的关联容器类型是map和set。map 中的元素是一些关键字—值对,关键字起到索引的作用,值是关键字相关联的数据。set 中每个元素为一个关键字,set 支持高效的关键字查询操作。
标准库提供8个关联容器:
容器类型 | 含义 |
---|---|
map | 关联数组,保存关键字—值对 |
set | 关键字即值,即只保存关键字的容器 |
multimap | 关键字可重复出现的map |
multiset | 关键字可重复出现的set |
unordered_map | 用哈希函数组织的map |
unordered_set | 用哈希函数组织的set |
unordered_multimap | 哈希组织的map,关键字可重复出现 |
unordered_multiset | 哈希组织的set,关键字可重复出现 |
类型 map 和 multimap 定义在头文件map中,set 和 multiset 定义在头文件set中,无序容器则分别定义在头文件unordered_map和unordered_set中。
虽然大多数人熟悉诸如vector和list这样的数据结构,但从未使用过关联结构。在学习标准库关联容器类型之前,我们首先来看一个如何使用这类容器的例子。map是关键字—值对的集合。例如可以将一个人的名字作为关键字,将其电话号码作为值。map 类型通常被称为关联数组,关联数组与正常数组类似,不同之处在于我们通过一个关键字而不是位置来查找值。给定一个名字到电话号码的 map,我们可以使用一个人的名字作为下标来获取此人的电话号码。与之相对,set 是关键字的集合。当只想知道一个值是否存在时,set 是最有用的。例如我们可以定义一个电话簿来检查那些人的号码存储在我们的电话簿中。
1.使用map
一个经典的使用关联数组的例子是单词计数程序:
map <string, size_t> word_count;
string word;
while(cin >> word)
++ word_count[word];
for(const auto & w : word_count)
cout << w.first << "occurs" << w.second <<((w.second>1) ? "times" : "time") << endl;
2.使用set
上一个示例程序的一个合理拓展是:忽略常见单词,如"the",“and”、"or"等。可以使用 set 保存想忽略的单词,只对不在集合中的单词统计出现次数。
map <string,size_t> word_count;
set<string>exclude = {
"the","but","and","or"};
string word;
while(cin >> word){
// 只统计不在exclude中的单词
if(exclude.find(word) == exclude.end())
++ word_count[word];
}
关联容器不支持顺序容器的位置相关操作,例如push_front或push_back。原因是关联容器中的元素是根据关键字存储的,这些操作对关联容器没有意义。而且,关联容器也不支持构造函数或插入操作这些接受一个元素值和一个数量值的操作。
当定义一个map时,必须即指明关键字类型又指明值类型,而定义一个set时,只需要指明关键字类型,因为 set 中没有值。每个关联容器都定义一个默认构造函数,它创建一个指定类型的空容器,我们可以将关联容器初始化为另一个同类型容器的拷贝,或是从一个值范围来初始化关联容器。只要这些值可以转化为容器所需类型就可以。
set<string> exclude = {
"the","but","and","or"};
map<string,string>authors = {
{
"john","jame"},
{
"aus","jane"},
{
"dick","chars"}
};
1.初始化multimap或multiset
一个 map 或 set 中的关键字必须是唯一的,即一个容器内关键字不会重复。容器multimap和multiset没有此限制,它们都允许多个值对应相同的关键字。例如,在我们用来统计单词数量的map中,每个单词只能有一个对应的值。另一方面,在一个词典中,一个特定单词则具有多个与之关联的值。
vector<int>ivec;
for(vector<int>::size_type i = 0;i != 10; ++i){
ivec.push_back(i);
ivec.push_back(i);
}
set<int>iset(ivec.cbegin(),ivec.cend());
multiset<int>miset(ivec.cbegin(),ivec.cend());
cout<<ivec.size()<<endl; //打印出20
cout<<iset.size()<<endl; //打印出10
cout<<miset.size()<<endl; //打印出20
关联容器对于包含的关键字类型有一些限制。对于有序容器—map、set 和 multimap、multiset,关键字类型必须定义元素比较的方法。默认情况下,标准库使用关键字类型的小于运算符来比较2个关键字。在set集合中,关键字类型就是元素类型;在map中,关键字类型是元素的第一部分的类型。
1.有序容器的关键字类型