第十一章 关联容器
1.关联容器与顺序容器有着根本的不同:关联容器中的元素是按照关键字来访问和保存的,而顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的。不同之处反映了关键字的作用。
2.关联容器支持高效的关键字查找和访问。两个最主要的关联容器是map和set。
3.map中的元素是一些关键字-值对:关键字起到索引的作用,值代表与索引相关联的数据。例如字典:单词为关键字,意思为值。
map<string,size_t> count;
map类型通常被称为关联数组,与普通数组的不同在于,map的下标不必是整数,可以通过一个关键字而不是位置来查找值。
set中的元素只包含一个关键字,set支持高效的关键字查询操作。例如:在文本处理中用set来保存想要忽略的单词。
set<int> num;
当想要知道一个值是否存在时,使用set是最有用的。
4.
map / set 不允许重复关键字
multimap / multiset 是允许重复关键字的容器(允许多个元素具有相同的关键字),元素是有序的
unordered_map / unorder_set 是元素无序保存的容器,不允许重复关键字
unordered_multimap / unordered_nultiset 是允许关键字重复,且无序的容器
5. 标准库类型pair,定义在头文件utility中,一个pair保存两个数据成员,pair<string,int> a;他们是public的,两个成员分别为first和second,用成员访问符号“.”来访问。即a.first 和a.second。
map的每个元素都是一个pair对象!
6. 关联容器关键字和值的类型:
key_value 此容器类型关键字类型
mapped_type 每个关键字所关联的值的类型,只适用于Map
value_type 对于set,与key_value相同;对于map,为pair<const key_type, mapped_type> 可以改变一个pair的值,但不能改变一个元素的关键字,是const的。
7. 当解引用一个关联容器迭代器时,会得到一个类型为容器的value_type的值的引用。
对于map来说是一个pair类型,对于set;来说是一个key_value类型。一个set关键字也是const的,只能读取不能修改。
例:
map<string, size_t> word_count;
auto map_it = word_count.begin();
//*map_it 指向一个pair<const string,size_t>对象的引用;
cout << map_it->first; //打印此元素的关键字
cout << map_it->second; //打印此元素的值
++map_it->second; // 即 (++(*map_it).second;)
set<int> set = { 1, 2, 3, 4, 5 };
auto set_it = set.begin();
cout << *set_it << endl; //读取关键字
set类型中的元素是const的,map中元素是pair,其第一个成员是const。所以通常不对关联容器使用泛型算法。而实现其它功能可以使用关联容器专门定义的函数成员。
8.添加元素
关联容器的insert成员向容器中添加一个元素或元素范围,由于map和set不包含重复的关键字,所以插入一个已存在的元素对容器没有影响。
insert有两个版本:接受一对迭代器和一个初始化列表
(1)set的添加:
vector<int> ivec = { 1, 2, 3, 4, 5, 6 };
set<int> sett;
sett.insert(ivec.cbegin(), ivec.cend());
sett.insert({ 7, 8, 9, 0 });
对于一个给定的关键字,只有第一个带此关键字的元素才被插入到容器中。
(2)map的添加:
对一个map进行insert操作时必须记得元素类型是pair。
map<string, size_t> word_count;
string word;
while (cin >> word){
auto ret = word_count.insert({ word, 1 }); 方法1
auto ret = word_count.insert(make_pair(word,1)); 方法2
auto ret = word_count.insert(pair<string,size_t>(word,1)); 方法3
auto ret = word_count.insert(map<string,size_t>::value_type(word,1)); 方法4
(3)insert返回值:
insert返回的值依赖于容器类型和参数,对于不包含重复关键字的容器,添加单一元素的insert版本返回一个pair,来说明是否插入成功。
pair的first成员是一个迭代器,指向具有给定关键字的元素;second成员是一个bool值,说明插入成功或者已存在于容器中。如果关键字已存在,返回false,如果不存在,但会true。
例:统计每个单词出现次数
map<string, size_t> word_count;
string word;
while (cin >> word){
auto ret = word_count.insert({ word, 1 }); //方法1
//auto ret = word_count.insert(make_pair(word,1)); 方法2
//auto ret = word_count.insert(pair<string,size_t>(word,1)); 方法3
//auto ret = word_count.insert(map<string,size_t>::value_type(word,1)); 方法4
if (!ret.second)
++ret.first->second;
}
输入第一个word,将word和其值1存入到map中,此时返回的pair的第二个成员bool返回ture,所以不进行之后的累加;如果之后再次输入同样的word,会将word和其计数值1再次存入map中,但此时Bool返回一个false.if的条件变为真,所以进行累加,讲1累加为2,说明这个word出现了2次。
++ret.first->second 即++((ret.first)->second);
ret是添加word之后返回的pair类型。
ret.first是一个迭代器,只想该pair中具有给定关键字的元素。(指向的是元素)
ret.first->second 解引用该元素( (*ret.first).second ),提取该关键字所关联的值(之前为1的那个值)。 之后进行累加1+1=2
9.删除元素
通过传递给erase一个迭代器或者一个迭代器范围来删除一个或一个元素范围。指定的元素被删除,返回void。
或者接受一个key_value参数,来删除给定关键字的元素,返回实际删除元素的数量。对于不重复的容器,返回值为0或者1。为0时表示该元素不在容器中。
word_count.erase(removal_word); 返回一个size_type的值,指出删除的元素的数量;
word_count.erase(p); p是一个迭代器,返回指向P之后元素的位置,若P指向尾元素,则返回尾后迭代器。
举例:删除multimap的同一作者的所有元素
#include<map>
#include<iostream>
#include<string>
#include<algorithm>
using namespace::std;
int main(){
multimap<string, string> test={{ "li", "a" }, { "gong", "b" }, { "he", "c" }, { "li", "d" }, {"gong","e"}};
string a1 = ("gong");
for (auto beg = test.equal_range(a1); beg.first!=beg.second;){
auto r=test.erase(beg.first);
beg.first=r;
}//方法一 用equal_range
for (auto beg = test.lower_bound(a1), end = test.upper_bound(a1); beg != end;){
auto r = test.erase(beg);
beg= r;
}//方法二 用lower 或者 upper
for (const auto& t : test)
std::cout << t.first << " " << t.second << std::endl;
return 0;
}
因为erase返回的迭代器的下一个位置,所以将其赋值给beg,而不用beg自己累加。
10. map的下标操作
map下标操作接受一个关键字,或许与此关键字关联的值。但是,与其他下标操作不同的是,如果关键字并不再map中,会为其创建一个元素并将其插入到map中,所关联的值将进行值初始化。所以,map下标操作有可能会插入一个新元素,只可以对非const的map使用下标操作。
除此之外,对map进行下标操作时,会获得一个mapped_type对象,也就是其关联的值。
当解引用一个map迭代器时,会得到一个value_type对象。如上7所述。
map的下标操作返回一个左值(一个对象或函数)
11. 访问元素
(1)word_count.find(k) 查找:返回一个迭代器,指向第一个关键字为K的元素,若K不在容器中,则返回word_count.end( )
(2)word_count.count(k) 计数:返回关键字等于K的元素的数量。对于不允许重复的迭代器,返回永远是0或1
(3)如果一个multimap或multiset中有多个元素具有给定的关键字,则这些元素会在容器中相邻存储。
word_count.lower_bound(k) 返回一个迭代器,指向第一个具有给定关键字的元素
word_count.upper_bound(k) 返回一个迭代器,指向最后一个具有给定关键字的元素之后的位置
lower_bound和upper_bound其不适用于无序容器;
如果元素不再multimap中,则他俩会返回相等的迭代器——指向不影响排序的关键字插入位置。一个安全位置。
(4)word_count.equal_range(k) 返回一个迭代器pair,表示关键字等于k的元素的范围,若k不存在,则pair的两个成员都是尾后迭代器
若关键字存在,则第一个迭代器指向第一个与关键字匹配的元素,第二个迭代器指向最后一个匹配元素之后的位置。
12. 无序容器
无序容器在存储上组织为一组桶,每个桶保存0个或者多个元素,无序容器使用一个哈希函数将元素映射到桶。容器将具有一个特定哈希值的所有元素都保存在相同的桶中。所以无序容器的性能依赖于哈希函数的质量和桶的数量和大小。