- 关联容器不支持顺序容器的位置相关操作,如 push_back或push_front。原因是关联容器中元素是按关键字存储的,这些操作对关联容器没有意义。
- 关联容器也不支持构造函数或插入操作这些接受一个元素值和一个数量值的操作
- 关联容器的迭代器都是双向的,还有一些关于哈希性能的操作。
按关键字有序保存元素
map | 关联数组;保存关键字-值对 |
---|---|
set | 关键字即值 |
multimap | 关键字可重复出现的map |
multiset | 关键字可重复出现的set |
当定义一个map时,必须既指明关键字类型又指明值类型;
无序集合
unordered_map | 用哈希函数组织的map |
---|---|
unordered_set | 用哈希函数组织的set |
unordered_multimap | 哈希组织的map;关键字可以重复出现 |
unordered_multiset | 哈希组织的set;关键字可以重复出现 |
//使用map统计单词数
int main(int argc,char **argv)
{
map<string,size_t> word_count;
fstream in("1.txt");
string word;
while(in>>word)
++word_count[word];
for(auto it=word_count.begin();it != word_count.end();it++)
{
cout<<it->first<<":";
cout<<it->second<<endl;
}
cout<<endl;
return 0;
}
关联容器的初始化与顺序容器类似,在此就不必详细介绍
有序容器在定义其关键字时,其关键字的类型必须包含元素比较的方法,如果是一个类类型,且没有包含比较方法,则不合法,可以自行定义比较类型。
当我们使用decltype作用于某个函数时,它返回函数类型而非指针类型,因此我们需要显示的加上*已表明我们需要返回指针。作用:选择并返回操作数的类型,若为函数,则类型为函数的返回类型
- 当从map提取元素时,会得到一个pair类型的对象,保存first和second的数据成员。可以使用列表对关联容器进行初始化;
- 关联容器不支持顺序容器的位置相关操作,如push_back,push_front,因为关联容器使用关键字存储。
- 关联容器的迭代器是双向的
pair类型
pair的默认构造函数对数据成员进行值初始化
pair<T1,T2> p | p是一个pair,两个类型分别为T1和T2的成员 |
---|---|
pair<T1,T2> p(v1,v2) | 分别用v1,v2来初始化first,second成员 |
pair<T1,T2> p={v1,v2} | 等价于p(v1,v2) |
make_pair(v1,v2) | 返回一个用v1,v2初始化的pair |
关联容器的操作
key_type | 此容器类型的关键字类型 |
---|---|
mapped_type | 每个关键字关联值的类型,只适用于map |
value_type | 对于set,与key_value相同 对于map,为pair<const key_type,mapped_type> |
- 对于map而言value_type是一个pair类型,其first成员保存const的关键字,second成员保存值,我们可以改变pair的值,但不能改变pair的关键字
- set的迭代器时const的,即使同时定义了iterator和const_iterator类型,但都只能只读访问元素
- 通常不对关联容器使用泛型算法,关键字是const这一特性意味着不能将关联容器传递给修改容器和重排元素的算法。
添加元素
对于map和set包含不重复的关键字,因此插入一个已存在的关键字没有影响
向map添加元素
对一个map容器进行insert操作时,必须记住元素类型是pair,通常需要构造一个pair再进行插入
word_count.insert({word,1});
word_count.insert(make_pair(word,1));
word_count.insert(pair<string,size_t>(word,1));
word_count.insert(map<string,size_t>::value_type(word,1));
c.insert(v) c.emplace(args) | v是value_type类型的对象; args用来构造一个元素 |
---|---|
c.insert(b,e) | b,e是一对迭代器,表示一个c::value_type类型值的范围 |
c.insert(li) | 插入一个列表 |
c.insert(p,v) | 将迭代器p作为一个指示,指示从哪里开始搜索新元素应该存储的位置 |
检测insert的返回值
对于不包含重复元素的关联容器插入时
insert的返回值依赖于容器类型和参数,添加单一元素的insert和emplace返回一个pair,告诉我们插入是否成功,pair的first是一个迭代器,指向具体给定关键字的元素,second的成员是一个bool值,指出元素是否已经成功插入到容器中,还是已经存在在容器中。
eg:
auto ret = v.insert({word,1});
ret 保存的是insert的返回值,是一个pair;
ret.first 是pair的第一个成员,是一个map迭代器,指向给定关键字的元素;
ret.first-> 解引用此迭代器,提取map中的元素,元素也是一个pair;
ret.first->second map中元素的值部分;
ret.second 是一个bool值
当向允许重复元素的容器添加元素时,insert返回值一个指向新元素的迭代器,这里不会返回一个bool值。
删除元素
c.erase(k) | 从c中删除每个关键字为k的元素。返回一个size_type值,指出删除元素的数量 |
---|---|
c.erase§ | 从c中删除迭代器p指向的元素,p必须指向一个真实元素。返回一个指向p之后元素的迭代器,若p指向c.end(),则返回c.end() |
c.erase(b,e) | 删除迭代器对b和e所表示的范围中的元素 |
map的下标操作
c[k] 返回一个关键字为k的元素,如果k不在c中的话,添加一个关键字为k的元素,对其进行值初始化。
c.at(k) 访问一个关键字为k的元素,带参数检查,如果k不存在抛出out_range_of异常
- 使用一个不在容器中的元素作为下标,会添加一个具有此关键字的元素到map中。
- set类型不支持下标操作,因为set中没有与关键字相关联的值。
- 当对一个map进行下标操作时,会获得一个mapped_type对象 ;但当解引用一个map迭代器时,会得到一个value_type对象;
访问元素
下标和at只适用于非const的map和unordered_map
c.find(k) | 返回一个迭代器,指向第一个关键字为k的元素,若k不在容器中,则返回一个尾后迭代器 |
---|---|
c.count(k) | 返回关键字等于k的数量,对于不允许重复关键字的容器,返回值永远为0或1 |
c.lower_bound(k) | 返回一个迭代器,指向第一个关键字不小于k的元素 |
c.upper_bound(k) | 返回一个迭代器,指向第一个大于k的元素 |
c.equal_range(k) | 返回一个迭代器pair,表示关键字等于k的元素的范围,若k不存在,pair的两个成员都等于end(); |
equal_range返回一个迭代器pair,若关键字存在,则第一个迭代器指向第一个与迭代器匹配的元素,第二个迭代器指向最后一个匹配元素之后的位置。
- 如果一个关键字不在map中,则lower_bound会返回关键字的第一个安全插入点——不影响容器的顺序插入位置。
- 如果lower_bound和upper_bound返回相同的容器,则给定关键字不在容器中
无序容器
无序容器在存储组织上为一个桶,每个桶保存零个或多个元素,无序容器使用一个哈希函数映射到桶,容器将具有一个特定哈希值的元素保存在相同的桶里。
桶接口
c.bucket_count() 正在使用的桶的数目
c.max_bucket_count() 最多能容纳的桶的数目
c.bucket_size(n) 第n个桶有多少个元素
c.bucket(k) 关键字为k的元素在哪个桶
桶迭代
local_iterator 可以用来访问桶中元素的迭代器类型
const_local_count 桶迭代器的const版本
c.begin(n),c.end(n) 桶n的首元素迭代和尾元素迭代
哈希策略
c.load_factor() 每个桶的平均元素
c.reserve(n) 重组存储,使得c可以保存n个元素且不必rehash
无序容器对关键字类型的要求
不能直接定义关键字类型为自定义类类型的无序容器,需要提供函数来替代==运算符和哈希计算函数