关联容器中的元素是按关键字来保存和访问的,顺序容器中的元素是按顺序和位置保存和访问的。
1.关联容器概述:
map和set是两种主要的关联容器。
map中的元素是一些关联字-值,set中每个元素质包含一个关键字。
按关键字有序保存元素 | |
map | 关联数组,保存关键字-值对 |
set | 只保存关联字的容器 |
multimap | 关键字可重复出现的map |
multiset | 关键字可重复出现的set |
无序集合 | |
unordered_map | 用哈希函数组织的map |
unordered_set | 用哈希函数组织的set |
unordered_multimap | 哈希组织的map,关键字可重复出现 |
unordered_multiset | 哈希组织的set,关键字可重复出现 |
关联容器的迭代器都是双向的。
set的元素类型就是关键字,map的元素类型是pair类型的对象。
一个map和set中关键字必须是唯一的。但是对于加multi前缀的,允许多个元素具有相同的关键字。
2.pair类型:
map的元素是pair:pair的数据成员是public的,两个成员分别命名为first(关键字)和second(对应值)。
pair<string, int> word_count; // 保存一个string和一个int
可以使用make_pair(v1, v2) 返回一个用v1和v2初始化的pair。
3.关键字类型的要求:
对于有序容器,关键字类型必须定义元素比较的方法。
可以向一个算法提供我们自己定义的比较操作,所提供的操作必须在关键字类型上定义一个严格弱序(可看作“小于等于”)。
为了指定使用自定义的操作,必须在定义关联容器类型时提供此操作的类型。
bool CompareIsbn(const Sale_data &lhs, const Sale_data &rhs){
return lhs.isdn() < rhs.isdn();
}
multiset<Sale_data, decltypr(CompareIsdn)*> set; // 必须加上一个*,来指出要使用一个给定函数类型的指针
4.关联容器操作:
- key_type:关键字类型
- mapped_type:关键字关联的类型,只适用于map
- value_type:元素的值得类型,map是paur类型。
使用作用于运算符来提取一个类型成员,例如:map<string, int>::key_value;
当解引用一个关联容器的迭代器时,会得到一个类型为容器的value_type的值得引用。
map和set的关键字是const的,所以不能修改。只能修改map的关联值。
迭代器是按照关键字升序遍历元素的。
通常不对关联容器使用算法,可用于只读算法。但使用关联容器专用的find会比调用泛型find快得多。
关联容器使用算法的话,要么作为源序列,要么作为目的位置。
5.添加元素:
map和set插入一个已存在元素,对容器没有影响。
set1.insert(ivec.cbegin(), ivec.cend()); // 还可以使用初始化列表{}插入
向map中插入元素,要先创建一个pair。
返回值:添加单一元素的insert和emplace返回一个pair。
ret.first:一个map迭代器,指向具有给定关键字的元素。ret.first->first 插入的关键字。
ret.second:一个bool值,插入成功为TRUE,已存在与列表中为FALSE。multi关联容器无需返回bool值,因为可以重复,所以总是向容器中插入。
6.删除元素:
关联容器可根据key_value删除元素。erase(key_value);
7.下标操作:
set没有下标操作。multi也没有下标操作,因为可以有相同的关键字。map和unordered_map可以使用at()。
可以使用[]的下标操作,如果该关键字不在map中,会为它创建一个元素插入到容器中。
由于下标操作可能插入元素,所以只能对非const的map使用。
顺序容器的解引用一个迭代器和下标访问的返回值是一样的,但是map的下标访问返回mapped_type(关联的值),解引用返回的是value_type(pair类型);
8.访问元素:
- count(key) :返回这个关键字出现的次数。
- upper_bound(key):返回一个迭代器,指向第一个关键字大于k的元素(不适用无序)
- lower_bound(key):返回一个迭代器,指向第一个关键字不小于k的元素(不适用无序)
- equal_range(k):返回一个pair,两个成员为迭代器,表示关键字为k的范围,没找到的话,都是尾后迭代器。
9.无序容器:
使用哈希函数和==运算符来组织元素(有序使用<)。
维护元素序列的代价通常很高,不需要排序时可使用无序容器
也可使用find和insert。
管理桶:无序容器在存储组织为一组桶,每个桶保存零个或多个元素,使用哈希函数将元素映射到桶。
为访问一个元素,容器先计算元素的哈希值,它指出应该搜索哪个桶。
容器将具有同一个哈希值的元素都存放在一个桶中(不同元素也可能映射到一个桶)。如果是multi的容器,相同的关键字元素也会在一个桶中。
无序容器的性能依赖于哈希函数的质量和桶的数量和大小。
使用一个hash<key_value>类型的对象来生成每个元素的哈希值。
关键字需要有hash模板。标准库为内置类型,string和智能指针提供了hash模板。自定义类型的无序容器,不能直接使用哈希模板,必须提供我们自己的hash模板版本。
size_t hasher(const Sale_data &sd){
return hash<string>()(sd.isdn()); // 通过isdn的hash模板来达到Sale_data的模板
}
bool eqOp(const Sale_data &l, const Sale_data &r){ return l.isdn() == r.isdn(); }
unordered_multiset<Sale_data, decltyle(hasher)*, decltype(eqOp)*> setBook;
如果自定义类已经定义了==操作符,则可以只重载哈希函数。