第十一章 关联容器
map 和 set
map 头文件map, set和multiset 头文件set, unordered 系列在在unordered_map, unordered_set中
map 键值对, set 只保存关键字(当只想知道一个值是否存在时, set是最有用的.)
带multi 表示关键字可以重复的, 带unordered 表示无序的
无序容器 使用哈希组织
map 关联数组, 却别在于map下标不必是整数
- map 关联数组 键值对
- set 关键字即值, 只保存关键字的容器
- multimap 关键字可重复的map
- multiset 关键字可重复的set
- unordered_map 用哈希函数组织的map
- unordered_set 用哈希函数组织的set
- unordered_multimap 哈希组织的map, 关键字可重复
- unordered_multiset 哈希组织的set, 关键字可重复
有序容器关键字的 必须遵循严格弱序 规则.
如果一个类没有定义< 运算符, 则不能使用multiset/ set , 可以使用类内可以比较的函数替代. 如不能直接定义Sales_data的multiset, 但是因为Sales_data中定义的compareIsbn 在isbn上定义了一个严格弱序. 此时定义multiset需要提供两个类型: 关键字类型以及比较操作类型 - 一种函数指针类型.
multiset<Sales_data, decltype(compareIsbn) *> bookstore(compareIsbn);
当使用decltype来获取函数指针类型时, 必须加上一个*来指出我们要需要一个给定函数类型的指针.
当使用一个函数的名字时, 在需要的情况下它会自动转化为一个指针. 类似于数组
关于 11.10: vector<int> 是双向迭代器, 其定义了比较运算符<, 可以作为 map的关键字. 而 list<int> 是双向迭代器, 没有定义<, 所以不能作为关键字
vector<int> -> _Vector_iterator<_Scary_val> -> _Vector_const_iterator<_Myvec> ->
_NODISCARD bool operator<(const _Vector_const_iterator& _Right) const noexcept {
_Compat(_Right);
return _Ptr < _Right._Ptr;
}
list<int> -> _List_iterator<_Scary_val> -> _List_const_iterator<_Mylist> 该类中未定义 < 运算符
pair类型(utility头文件)
make_pair(key, value) 返回一个pair
pair和map的区别: map 是容器 pair 是一种类型
关联容器的操作
- key_type 容器类型的关键字类型 严格弱序
- mapped_type 每个关键字关联的类型 只适用于map
- value_type map: 是一个pair<const key_type, mapped_type>
此处与通常的key-value类型有所不同, 通常意义上的key 则使用key_type, value使用value_type 来获取类型, 而此处不同的是value 为mapped_type, value则指的是<key, value>的pair类型.
一个map的value_type 是一个pair, 可以改变pair的值, 但不能改变关键字.
set的迭代器是const的.
添加元素:
map/set 插入一个关键字已经存在的元素 则没有任何影响.
- mapx.insert({word, 1})
- mapx.insert(make_pair(word,1));
- mapx.insert(pair<string, size_t>(word,1));
- mapx.insert(map<string,size_t>::value_type(word,1));
- mapx.emplace(args) 返回一个pair, 包含一个迭代器和插入是否成功的bool值
删除元素
- mapx.erase(k) 删除关键字是k的所有元素, 返回删除元素的个数size_type
- mapx.erase(p) 删除迭代器p指定的元素. 返回指向p之后元素的迭代器.
- mapx.erase(b,e) 删除迭代器b e表示的范围, 返回e
map的下标操作
如果下标不存在则会创建该下标为关键字的元素. 如果只是判断关键字是否存在,则一定不能使用下标
map.at(k) 不存在k, 则会抛出out_of_range的异常
下标运算返回值是mapped_type对象, 解引用获取的是value_type 对象, 可以这么理解, 下标运算代表已经知道了关键字, 则无需返回关键字, 而解引用 则不知道关键字和值, 所以返回pair中第一部分为关键字, 第二部分为值
访问元素
- 下标, at 只适用 非const的map和unordered_map
- c.find(k) 返回找的迭代器, 否则end()
- c.count(k) 返回 k的个数
- c.lower_bound(k) 返回一个迭代器, 指向第一个关键字不小于k的元素 有序容器
- c.upper_bound(k) 返回一个迭代器, 指向第一个关键字大于k元素 有序容器
- c.equal_range(k) 返回迭代器pair, 关键字等于k的元素范围, k不存在, pair的两个成员都等于end()
lower_bound, upper_bound 相当于过滤关键字的所有元素, 返回是迭代器.
beg = lower_bound(k), end = upder_bound(k);
假如有多个元素与给定关键字匹配, 则beg指向第一个元素, 通过递增beg来遍历这些关键字元素, 当beg等于end时, 遍历完成
multimap, multiset 相同关键字的元素会相邻存储
equal_range(k) 根据关键字k返回一个迭代器pair, 当pair中的两个迭代器相同时, 则没有该关键字, 否则两个迭代器的范围就是所有的关键字元素.
无序容器
无序容器提供了和有序容器相同的操作: find, insert等.
管理桶
无序容器管理操作
桶接口:
- c.bucket_count() 正在使用的桶的数量
- c.max_bucket_count() 容器能容纳的最多的桶数量
- c.bucket_size(n) 第n个桶中有多少个元素
- c.bucket(k) 关键字为k的元素在哪个桶中
桶迭代:
- local_iterator 可以用来访问桶中元素的迭代器类型
- const_local_iterator
- c.begin(n), c.end(n) 桶n的首元素迭代器和尾后迭代器
- c.cbegin(n), c.cend(n)
哈希策略:
- c.load_factor() 每个桶的平均元素数量, 返回float值
- c.max_load_factor() 容器有可能会自动维护平均桶的数量, 但是桶数量<= max_load_factor的值
- c.rehash(n) 重组存储, 使得bucket_count >=n 且 bucket_count > size/max_load_factor
- c.reserve(n) 重组存储, 使得容器可以保存n个元素且不必rehash.
无序容器自定hash函数和 ==
size_t hasher(const Sales_data& sd) {
return hash<string>()(sd.isbn());
}
bool eqOp(const Sales_data& lhs, const Sales_data& rhs) {
return lhs.isbn() == rhs.isbn();
}
int main(int argc, char** argv) {
using SD_multiset = unordered_multiset<Sales_data, decltype(hasher)*, decltype(eqOp)*>;
SD_multiset bookstore(42, hasher, eqOp);
本文介绍了C++中map、set及其unordered系列容器的使用,包括它们的特点(如关联数组、关键字唯一或多、有序/无序),以及如何通过自定义哈希函数和比较操作进行操作。重点讲解了map和set的迭代、添加删除、下标访问及无序容器的管理技巧。
684

被折叠的 条评论
为什么被折叠?



