文章目录

  • 使用关联容器
    • map示例
  • 关联容器概述
    • 定义关联容器
      • 关联容器值初始化
      • multimap和multiset
    • 关键字类型的要求
    • pair类型
      • pair上的操作
  • 关联容器操作
    • 关联容器额外的类型别名
    • 关联容器迭代器
      • map迭代器
      • set迭代器
      • 关联容器和算法
    • 添加元素
      • 向map添加元素
      • 检测insert的返回值
      • 使用insert代替下标操作写单词计数程序
    • 删除元素
    • map的下标操作
    • 访问元素
      • multimap查找元素代码示例一:count和find
      • multimap查找元素代码示例二:lower_bound和upper_bound
      • multimap查找元素代码示例三:equal_range函数
  • 无序容器
    • 无序容器管理操作

关联容器和顺序容器有着根本的不同:关联容器中的元素是按关键字来保存和访问的,顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的。
两个主要的管理容器类型是map和set。map中的元素是key-value对,set中每个元素只包含一个关键字,set支持高效的关键字查询操作——检查一个给定关键字是否在set中。

使用关联容器

map示例

 map<string, size_t>word_count;string word;while (cin>>word) {++word_count[word];}for (const auto &wc:word_count) {cout << wc.first << " 出现的次数:" << wc.second << endl;}

输入:

hello
hi
haha
hello
haha
ha
^Z

输出:

ha 出现的次数:1
haha 出现的次数:2
hello 出现的次数:2
hi 出现的次数:1

关联容器概述

关联容器不支持顺序容器的位置相关的操作,例如push_front或push_back。关联容器也不支持构造函数或插入操作这些接受一个元素值和一个数量值的操作。

定义关联容器

关联容器值初始化

set<string>st={"set","the","hello"};
map<string,string>mp={{"zhang","san"},{"wang","wu"},{"li","si"}};

multimap和multiset

multimap和multiset允许关键字重复。

关键字类型的要求

关联容器对其关键字类型有一些限制。对于有序容器——map、multimap、set以及multiset,关键字类型必须定义元素比较的方法。
例如,我们不能直接定义Sales_data的multiset,因为Sales_data没有<运算符,但是可以用compareIsbn函数来定义一个multiset,次函数在Sales_data对象的ISBN成员上定义了一个严格弱序。

bool compareIsbn(const Sales_data &lhs,const Sales_data &rhs){return lhs.isbn()<rhs.isbn();
}
multiset<Sales_data,decltype(compareIsbn)*>bookstore(compareIsbn);

此处我们使用decltype来指出自定义操作的类型。当用decltype来获得一个函数指针类型时,必须加上一个*来指出我们要使用一个给定函数类型的指针。用compareIsbn来初始化bookstore对象,表示当我们向bookstore添加元素时,通过调用compareIsbn来为这些元素排序。可以用compareIsbn代替&compareIsbn作为构造函数的参数,因为当我们使用一个函数的名字时,在需要的情况下它会自动转化为一个指针,当然,使用&compareIsbn的效果也是一样的。

pair类型

标准库类型pair定义在头文件utility中。
一个pair保存两个数据成员,类似容器,pair是一个用来生成特定类型的模板。
例如: pair<string,int>p;
与其他标准库类型不同,pair的数据成员是public的,两个成员名分别为first和second。

pair上的操作

关联容器操作

关联容器额外的类型别名

set<string>::value_type v1;       v1是一个string
set<string>::key_type v2;         v2是一个string
map<string,int>::key_type v3;     v3是一个string
map<string,int>::mapped_type v4;  v4是一个int
map<string,int>::value_type v5;   v5是一个pair<const string ,int>

v5是一个pair<const string ,int>,关键字是const,不能改变

关联容器迭代器

map迭代器

当解引用一个关联容器迭代器时,我们会得到一个类型为容器的value_type的值的引用。下述代码不能执行语句
it->first=“newKey”,因为关键字是const!!
但可执行语句 it->second = 10;

 map<string, int>mp{ { "a",1 },{ "b",2 },{ "c",3 } };auto it = mp.begin();//(*it).first与it->first等价//迭代器it即为一个pair类型cout << (*it).first<< " "<<(*it).second << endl;cout << it->first << " " << it->second << endl;//可以通过迭代器更改元素it->second = 10;cout << it->first << " " << it->second << endl;it++;cout << it->first << " " << it->second << endl;

输出结果:

a 1
a 1
a 10
b 2

set迭代器

与不能改变一个map元素的关键字一样,set中的关键字也是const的,可以用一个set迭代器来读取元素的值,但不能修改:

 set<int>st{1,3,5,7,9,2,3,4,6,5};auto it = st.begin();while (it!=st.end()) {//*it = 100;  此句错误,关键字是const的,只能读不能修改cout << *it << " ";it++;}

输出结果:

1 2 3 4 5 6 7 9

关联容器和算法

我们通常不对关联容器使用泛型算法。关键字是const这一特性意味着不能将关联容器传递给修改或重排容器元素的算法,因为这类算法需要向元素写入值,而set类型中的元素是const的,map中的元素是pair,且其第一个成员是const的。
关联容器可用于只读取元素的算法,但是很多这类算法都要搜索序列。关联容器定义了一个名为find的成员,它通过一个给定的关键字直接获取元素,我们可以用泛型find算法来查找一个元素,但此算法会进行顺序搜索。使用关联容器定义的专用的find成员会比调用泛型find快得多。
在实际编程中,如果我们真要对一个关联容器使用算法, 要么是将它当做一个源序列,要么当做一个目的位置。例如可以用泛型copy算法将元素从一个关联容器拷贝到另一个序列。类似的,可以调用inserter将一个插入器绑定到一个关联容器。通过使用inserter,我们可以将关联容器当做一个目的位置来调用另一个算法。

添加元素

向map添加元素

向map用insert添加元素的四种方法:

map<string,int>wc;
string words="hello";
wc.insert({words,1});
wc.insert(make_pair(words,1));
wc.insert(pair<string,int>(words,1));
wc.insert(map<string,int>::value_type(words,1));

检测insert的返回值

添加单一元素的insert和emplace版本返回一个pair,pair的first成员是一个迭代器,指向具有给定关键字的元素;second成员是一个bool值,指出元素是插入成功还是已经存在于容器中,如果插入成功,则返回true,否则返回false。

 map<string,int>mp{ { "b",2 },{ "a",1 },{ "c",3 } };auto it1 = mp.insert({ "b",10 });//it1是一个paircout << it1.second<<endl;auto mp1 = it1.first;  //mp1是一个迭代器cout << mp1->first << " "<<mp1->second << endl;auto it2 = mp.insert({ "a2",10 });cout << it2.second << endl;auto mp2 = it2.first;cout << mp2->first << " " << mp2->second << endl;

输出结果:

0
b 2
1
a2 10

第一次插入失败,insert返回的迭代器指向map已存在的元素。
第二次插入成功,insert返回的迭代器指向插入的元素。

而对于允许重复的multimap,insert操作返回一个指向新元素的迭代器,无需返回bool值,因为insert总是向这类容器中加入一个新元素。

使用insert代替下标操作写单词计数程序

 map<string, int>mp;string str;while (cin>>str) {auto ret = mp.insert({ str,1 });if (!ret.second) {auto tmp = ret.first;(tmp->second)++;}}for (auto mp3 : mp) {cout << mp3.first << " " << mp3.second << endl;}

输入:

hello
hi
haha
hahaha
haha
hi
hello
hello
helll
hehehe
^Z

输出:

haha 2
hahaha 1
hehehe 1
helll 1
hello 3
hi 2

删除元素

关联容器定义了三个版本的erase,如图所示:

与顺序容器一样,我们可以通过传递给erase一个迭代器或一个迭代器对来删除一个元素或者一个元素范围。

 map<string, int>mp{ { "b",2 },{ "a",1 },{ "c",3 } };auto it =mp.erase("a");//将{ "a",1 }删除,it是删除的元素的数量,此处是1
 map<string, int>mp{ { "b",2 },{ "a",1 },{ "c",3 } };auto it =mp.erase(mp.begin());//将mp.begin()指向的元素删除,it是一个迭代器指向删除元素之后元素的迭代器//此处mp.begin()指向{ "a",1 },it指向{ "b",2 }

map的下标操作

map和unodered_map容器提供了下标运算符和一个对应的at函数,set类型不支持下标,因为set中没有与关键字相关联的“值”。我们不能对一个multimap或一个unodered_multimap进行下标操作,因为这些容器中可能有多个值与一个关键字相关联。

map下标运算符接受一个索引(即,一个关键字),获取与此关键字相关联的值。但是,与其他下标运算符不同的是,如果关键字并不在map中,会为它创建一个元素并插入到map中,关联值将进行值初始化。例如下述代码,mp["d"];使得mp中增加了元素{ “d”,0 }。

 map<string, int>mp{ { "b",2 },{ "a",1 },{ "c",3 } };for (auto mp3 : mp) {cout << mp3.first << " " << mp3.second << endl;}cout << endl;mp["d"];for (auto mp3 : mp) {cout << mp3.first << " " << mp3.second << endl;}

输出结果:

a 1
b 2
c 3a 1
b 2
c 3
d 0

由于下标运算符可能插入一个新元素,我们只可以对非const的map使用下标操作。
与vector和string不同,map的下标运算符返回的类型(mapped_type)与解引用map迭代器得到的类型(value_type)不同。

访问元素


multimap查找元素代码示例一:count和find

给定一个作者到著作的映射,打印一个特定的作者的所有著作

string item("luxun");
auto entries = authors.count(item);
auto iter = authors.find(item);
while(entries){cout<<iter->second<<endl;++iter;--entries;
}

multimap查找元素代码示例二:lower_bound和upper_bound

可以用lower_bound和upper_bound解决此问题。lower_bound返回的迭代器将指向第一个具有给定关键字的元素,而upper_bound返回的迭代器则指向最后一个匹配给定关键字的元素之后的位置。因此用相同的关键字调用lower_bound和upper_bound会得到一个迭代器范围,表示所有具有该关键字的元素。如果关键字不存在,则lower_bound和upper_bound指向相同的位置,迭代器范围即为空。
重写代码示例一的程序如下:

for(auto beg = authors.lower_bound(item),end=authors.upper_bound(item);
beg!=end;++beg){cout<<beg->second<<endl;
}

multimap查找元素代码示例三:equal_range函数

equal_range:接受一个关键字,返回一个迭代器pair,第一个迭代器指向第一个与关键字匹配的元素,第二个迭代器指向最后一个匹配元素之后的位置,若未找到匹配元素,则两个迭代器都指向关键字可以插入的位置,即upper_bound返回的迭代器的位置。

for(auto pos = authors.equal_range(item);
pos.first!=pos.second;++pos.first){cout<<pos.first->second<<endl;
}

无序容器

unordered_map
unordered_set
unordered_multimap
unordered_multiset
无序容器在存储上组织为一组桶,每个桶保存零个或多个元素。无序容器使用一个哈希函数将元素映射到桶。为了访问一个元素,容器首先计算元素的哈希值,它指出应该搜索哪个桶。容器将具有一个特定哈希值的所有元素都保存在相同的桶中。如果容器允许重复关键字,所有具有相同关键字的元素也都会在同一个桶中。因此,无序容器的性能依赖于哈希函数的质量和桶的数量和大小。

无序容器管理操作

C++ primer 第11章 关联容器相关推荐

  1. C++primer第十一章 关联容器 11.3关联容器操作 11.4 无序容器

    11.3关联容器操作 除了表9.2(第295页)中列出的类型,关联容器还定义了表11.3中列出的类型.这些类型表示容器关键字和值的类型. 对于set类型,key_type和value type是一样的 ...

  2. C++primer第十一章 关联容器 11.1使用关联容器 11.2 关联容器概述

    关联容器和顺序容器有着根本的不同:关联容器中的元素是按关键字来保存和访问的.与之相对,顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的. 虽然关联容器的很多行为与顺序容器相同,但其不同之处反映 ...

  3. C++ primer 11章关联容器

    map set multimap (关键字可重复出现) multiset 无序 unordered_map  (用哈希函数组织的map) unordered_set unordered_multima ...

  4. 《C++Primer》第九章-顺序容器-学习笔记(1)-顺序容器定义与操作

    <C++Primer>第九章-顺序容器-学习笔记(1) 文章目录 <C++Primer>第九章-顺序容器-学习笔记(1) 摘要 顺序容器的定义 容器元素的初始化 将一个容器初始 ...

  5. 【C++ Primer 第11章】2. 关联容器操作

    练习答案 一.访问元素 关联容器额外类型别名  key_type 此容器类型的关键字类型 mapped_type 每个关键字关联的类型,只 适用于map mapped_type 对于set,与key_ ...

  6. 《C++ Primer 第5版》-11.2关联容器概述-康奈尔笔记

    引入:2018年10月看<C++ Primer 第5版>而写的简单笔记 11.2.1定义关联容器 关联容器概述 定义关联容器 初始化multimap或multiset map<str ...

  7. C++primer十万字笔记 第十一章 关联容器

     关联容器支持高效的关键字查找和访问,两个主要的关联容器是map和set.map中的元素是一些关键字-值(key-value)对:关键字起到索引的作用,值表示与索引相关联的数据.set中每个元素只包含 ...

  8. C++ Primer 5th笔记(chap 11)关联容器---无序容器

    无序关联容器 unordered associative container •unordered_map •unordered_set •unordered_multimap •unordered_ ...

  9. C++ Primer 5th笔记(chap 11)关联容器

    •map •multimap •set •multiset •set •unordered_map •unordered_set •unordered_multimap •unordered_mult ...

最新文章

  1. h1.1 hadoop简介
  2. Cassandra学习手册之一:Cassandra介绍
  3. 【设计模式】访问者模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 )
  4. 捕获计算机屏幕++方法,如何在Windows 10计算机上录制屏幕以及如何捕获计算机的音频...
  5. VirtualBox虚拟机Ubuntu设置共享文件夹
  6. python共享文件权限_利用Python实现在同一网络中的本地文件共享方法
  7. 用VisualBrush定制复杂的按钮样式
  8. C# winform+ springboot + mybatis 分页查询
  9. 手机交互应用服务(邮件)
  10. 平分物品价值java_网易互联网8.8笔试_第2题平分物品_自己的题解记录
  11. LINUX查看一个进程用的内存准确数量
  12. C#、VB.NET与三菱Q02、QJ71E71、L02、LJ71E71、FX3U、FX5U等通讯的DLL及调用源代码
  13. 计算机二进制拨码,8位二进制拨码对照表图片
  14. 将vscode改成中文界面
  15. Django restframework重写get_serializer_class方法自定义serializer_class
  16. 使用axis调用WebService,Java WebService调用工具类
  17. Flask项目1(美食地图)
  18. ICASSP 2022 | 前沿音视频成果分享:基于可变形卷积的压缩视频质量增强网络
  19. 事还得慢慢做,环境还得靠自己准备
  20. [转]谈谈 Bias-Variance Tradeoff

热门文章

  1. arcgis批量将栅格里的nodata转为0
  2. LSGO软件技术团队内部技术交流【2015-2016(1)第七周】
  3. 【转】3:C#异步WaitAll的使用
  4. 【转】刨根究底字符编码之十二——UTF-8究竟是怎么编码的
  5. ASP.Net请求处理机制初步探索之旅 - Part 2 核心
  6. 第五节:WebApi的三大过滤器
  7. 【计蒜客 - 程序设计竞赛】商业信息共享(Tarjan缩点)
  8. 用生动的例子花式解释:python类中一定需要有 __init__方法么?没有会怎样?
  9. html在表格添加下拉按钮,Bootstrap-table 使用说明--如何在表格td里增加一个按钮
  10. php session和cookie区别,php中session和cookie的区别是什么?