C++ 关联容器
《C++ Primer 4th》读书笔记
关联容器和顺序容器的本质差别在于:关联容器通过键(key)存储和读取元素,而顺序容器则通过元素在容器中的位置顺序存储和访问元素。
关联容器(Associative containers)支持通过键来高效地查找和读取元素。两个基本的关联容器类型是 map set。map 的元素以键-值(key-value)对的形式组织:键用作元素在 map 中的索引,而值则表示所存储和读取的数据。set仅包含一个键,并有效地支持关于某个键是否存在的查询。
关联容器类型 |
|
map |
关联数组:元素通过键来存储和读取 |
set |
大小可变的集合,支持通过键实现的快速读取 |
multimap |
支持同一个键多次出现的 map 类型 |
multiset |
支持同一个键多次出现的 set 类型 |
pair 类型在 utility 头文件中定义。
pairs 类型 |
提供的操作 |
pair<T1, T2> p1; |
创建一个空的 pair 对象,它的两个元素分别是 T1 和 T2类型,采用值初始化 |
pair<T1, T2> p1(v1, v2); |
创建一个 pair 对象,它的两个元素分别是 T1 和 T2 ,其中 first 成员初始化为 v1,而 second 成员初始化为 v2 |
make_pair(v1,v2) |
以 v1 和 v2 值创建一个新 pair 对象,其元素类型分别是v1 和 v2 的类型 |
p1 < p2 |
两个 pair 对象之间的小于运算,其定义遵循字典次序:如果 p1.first < p2.first 或者!(p2.first < p1.first) &&p1.second < p2.second,则返回 true |
p1 == p2 |
如果两个 pair 对象的 first 和 second 成员依次相等,则这两个对象相等。该运算使用其元素的 == 操作符 |
p.first |
返回 p 中名为 first 的(公有)数据成员 |
p.second |
返回 p 的名为 second 的(公有)数据成员 |
pair<string, string> anon; // holds two strings pair<string, int> word_count; // holds a string and an int pair<string, vector<int> > line; // holds string and vector<int>
与其他标准库类型不同,对于 pair 类,可以直接访问其数据成员:其成员都是仅有的,分别命名为 first 和 second。只需使用普通的点操作符——成员访问标志即可访问其成员:
string firstBook;
// access and test the data members of the pair
if (author.first == "James" && author.second == "Joyce")
firstBook = "Stephen Hero";
关联容器共享大部分——但并非全部的顺序容器操作。关联容器不提供front, push_front, pop_front, back, push_back 以及 pop_back 操作。
“容器元素根据键的次序排列”这一事实就是一个重要的结论:在迭代遍历关联容器时,我们可确保按键的顺序的访问元素,而与元素在容器中的存放位置完全无关。
map 类型
map 是键-值对的集合。map 类型通常可理解为关联数组(associative array):可使用键作为下标来获取一个值,正如内置数组类型一样。而关联的本质在于元素的值与某个特定的键相关联,而并非通过元素在数组中的位置来获取。
map的构造函数 |
|
map<k, v> m; |
创建一个名为 m 的空 map 对象,其键和值的类型分别为 k 和 v |
map<k, v> m(m2); |
创建 m2 的副本 m,m 与 m2 必须有相同的键类型和值类型 |
map<k, v> m(b, e); |
创建 map 类型的对象 m,存储迭代器 b 和 e 标记的范围内所有元素的副本。元素的类型必须能转换为 pair<const k, v> |
键类型的约束在使用关联容器时,它的键不但有一个类型,而且还有一个相关的比较函数。默认情况下,标准库使用键类型定义的 < 操作符来实现键(key type)的比较。对于键类型,唯一的约束就是必须支持 < 操作符,至于是否支持其他的关系或相等运算,则不作要求。
map 对象的元素是键-值对value_type 是存储元素的键以及值的 pair 类型,而且键为 const。
map类定义的类型 |
|
map<K,V>::key_type |
在 map 容器中,用做索引的键的类型 |
map<K,V>::mapped_type |
在 map 容器中,键所关联的值的类型 |
map<K,V>::value_type |
一个 pair 类型,它的 first 元素具有 const map<K,V>::key_type 类型,而 second 元素则为 map<K,V>::mapped_type 类型 |
map 迭代器进行解引用将产生 pair 类型的对象
对迭代器进行解引用时,将获得一个引用,指向容器中一个 value_type 类型的值。对于 map 容器,其 value_type 是 pair 类型:
// get an iterator to an element in word_count map<string, int>::iterator map_it = word_count.begin(); // *map_it is a reference to a pair<const string, int> object cout << map_it->first; // prints the key for this element cout << " " << map_it->second; // prints the value of the element map_it->first = "new key"; // error: key is const ++map_it->second; // ok: we can change value through an iterator
使用下标访问 map 对象并添加键-值元素对
1. 在 word_count 中查找键为 Anna 的元素,没有找到。
2. 将一个新的键-值对插入到 word_count 中。它的键是 const string 类型的对象,保存 Anna。而它的值则采用值初始化,这就意味着在本例中值为 0。
3. 将这个新的键-值对插入到 word_count 中。
4. 读取新插入的元素,并将它的值赋为 1。
使用下标访问 map 与使用下标访问数组或 vector 的行为截然不同:用下标访问不存在的元素将导致在 map 容器中添加一个新元素,它的键即为该下标值。
map <string, int> word_count; // empty map
// insert default initialzed element with key Anna; then assign 1 to its value
word_count["Anna"] = 1;
map 迭代器返回 value_type 类型的值——包含 const key_type 和mapped_type 类型成员的 pair 对象;下标操作符则返回一个 mapped_type 类型的值。
map 容器提供的 insert 操作 |
|
m.insert(e) e |
是一个用在 m 上的 value_type 类型的值。如果键(e.first)不在 m 中,则插入一个值为 e.second 的新元素;如果该键在 m 中已存在,则保持 m 不变。该函数返回一个pair 类型对象,包含指向键为 e.first 的元素的 map 迭代器,以及一个 bool 类型的对象,表示是否插入了该元素 |
m.insert(beg,end) |
beg 和 end 是标记元素范围的迭代器,其中的元素必须为m.value_type 类型的键-值对。对于该范围内的所有元素,如果它的键在 m 中不存在,则将该键及其关联的值插入到 m。返回 void 类型 |
m.insert(iter,e) |
e 是一个用在 m 上的 value_type 类型的值。如果键(e.first)不在 m 中,则创建新元素,并以迭代器 iter 为起点搜索新元素存储的位置。返回一个迭代器,指向 m 中具有给定键的元素 |
// if Anna not already in word_count, inserts new element with value 1 word_count.insert(map<string, int>::value_type("Anna", )); 传递给 insert 的实参相当笨拙。可用两种方法简化:使用 make_pair: word_count.insert(make_pair("Anna", )); 或使用 typedef typedef map<string,int>::value_type valType; word_count.insert(valType("Anna", ));
insert 的返回值
如果试图插入的元素所对应的键已在容器中,则 insert 将不做任何操作。带有一个键-值 pair 形参的 insert 版本将返回一个值:包含一个迭代器和一个 bool 值的 pair 对象,其中迭代器指向 map 中具有相应键的元
素,而 bool 值则表示是否插入了该元素。
// count number of times each word occurs in the input map<string, int> word_count; // empty map from string to int string word; while (cin >> word) { // inserts element with key equal to word and value 1; // if word already in word_count, insert does nothing pair<map<string, int>::iterator, bool> ret = word_count.insert(make_pair(word, )); if (!ret.second) // word already in word_count ++ret.first->second; // increment counter }
查找并读取 map 中的元素
使用下标存在一个很危险的副作用:如果该键不在 map 容器中,那么下标操作会插入一个具有该键的新元素。
map 容器提供了两个操作:count 和 find,用于检查某个键是否存在而不会插入该键。
不修改map 对象的查询操作 |
|
m.count(k) |
返回 m 中 k 的出现次数 |
m.find(k) |
如果 m 容器中存在按 k 索引的元素,则返回指向该元素的迭代器。如果不存在,则返回超出末端迭代器 |
map 容器的 erase 操作返回 void,而顺序容器的 erase 操作则返回一个迭代器,指向被删除元素后面的元素。
从map 对象中删除元素 |
|
m.erase(k) |
删除 m 中键为 k 的元素。返回 size_type 类型的值,表示删除的元素个数 |
m.erase(p) |
从 m 中删除迭代器 p 所指向的元素。p 必须指向 m 中确实存在的元素,而且不能等于 m.end()。返回 void |
m.erase(b,e) |
从 m 中删除一段范围内的元素,该范围由迭代器对 b 和 e 标记。b 和 e 必须标记 m 中的一段有效范围:即 b 和 e 都必须指向m中的元素或最后一个元素的下一个位置。而且,b 和 e 要么相等(此时删除的范围为空),要么 b 所指向的元素必须出现在 e 所指向的元素之前。返回 void 类型 |
与其他容器一样,map 同样提供 begin 和 end 运算,以生成用于遍历整个容器的迭代器。
set 容器支持大部分的 map 操作。两种例外包括:set 不支持下标操作符,而且没有定义 mapped_type 类型。在 set 容器中,value_type 不是 pair 类型,而是与 key_type 相同的类型。它们指的都是 set 中存储的元素类型。
可使用 insert 操作在 set 中添加元素:
set<string> set1; // empty set set1.insert("the"); // set1 now has one element
另一种用法是,调用 insert 函数时,提供一对迭代器实参,插入其标记范围内所有的元素。
set<int> iset2; // empty set iset2.insert(ivec.begin(), ivec.end()); // iset2 has 10
elements
与 map 容器的操作一样,带有一个键参数的 insert 版本返回 pair 类型对象,包含一个迭代器和一个 bool 值,迭代器指向拥有该键的元素,而 bool 值表明是否添加了元素。使用迭代器对的 insert 版本返回 void 类型。
为了通过键从 set 中获取元素,可使用 find运算。如果只需简单地判断某个元素是否存在,同样可以使用 count 运算,返回 set 中该键对应的元素个数。
multimap 和 multiset 类型
multimap和 multiset 类型与相应的单元素版本具有相同的头文件定义:分别是 map 和set 头文件。
multimap 和 multiset 所支持的操作分别与 map 和 set 的操作相同,只有一个例外:multimap 不支持下标运算。不能对 multimap 对象使用下标操作,因为在这类容器中,某个键可能对应多个值。
由于键不要求是唯一的,因此每次调用 insert 总会添加一个元素。带有一个键参数的 erase 版本将删除拥有该键的所有元素,并返回删除元素的个数。
在 multimap 和 multiset 中查找元素
关联容器 map 和 set 的元素是按顺序存储的。而 multimap 和multset 也一样。因此,在 multimap 和 multiset 容器中,如果某个键对应多个实例,则这些实例在容器中将相邻存放。
使用 find 和 count 操作
count 函数求出某键出现的次数,而 find 操作则返回一个迭代器,指向第一个拥有正在查找的键的实例:
// author we'll look for string search_item("Alain de Botton"); // how many entries are there for this author typedef multimap<string, string>::size_type sz_type; sz_type entries = authors.count(search_item); // get iterator to the first entry for this author multimap<string,string>::iterator iter = authors.find(search_item); // loop through the number of entries there are for this author for (sz_type cnt = ; cnt != entries; ++cnt, ++iter) cout << iter->second << endl; // print each title
返回迭代器的关联容器操作 |
|
m.lower_bound(k) |
返回一个迭代器,指向键不小于 k 的第一个元素 |
m.upper_bound(k) |
返回一个迭代器,指向键大于 k 的第一个元素 |
m.equal_range(k) |
返回一个迭代器的 pair 对象它的 first 成员等价于 m.lower_bound(k)。而 second 成员则等价于 m.upper_bound(k) |
lower_bound 返回的迭代器不一定指向拥有特定键的元素。如果该键不在容器中,则 lower_bound 返回在保持容器元素顺序的前提下该键应被插入的第一个位置。
// definitions of authors and search_item as above // pos holds iterators that denote range of elements for this key pair<authors_it, authors_it> pos = authors.equal_range(search_item); // loop through the number of entries there are for this author while (pos.first != pos.second) { cout << pos.first->second << endl; // print each title ++pos.first; }
C++ 关联容器的更多相关文章
- STL之关联容器
关联容器包含map.set.multimap.multiset. 关联容器的特点是明显的,相对于顺序容器,有如下特点: 1.其内部是采用非线性的二叉树结构,具体的说是红黑树的结构原理实现的. 2.se ...
- STL_关联容器 VS C++ hashmap
红黑树和哈希表区别: http://m.blog.csdn.net/article/details?id=52133283 关于STL中关联容器的几个问题: (1)为何map和set的插入删除效率比用 ...
- C++学习基础四——顺序容器和关联容器
—顺序容器:vector,list,queue1.顺序容器的常见用法: #include <vector> #include <list> #include <queue ...
- C++ Primer : 第十一章 : 关联容器之关联容器的迭代器和操作
关联容器的操作 除了和顺序容器定义的类型之外,关联容器还定义了一下几种类型: 关联容器额外的类型别名 key_type 此容器类型的关键字类型 mapped_type 每个关键字关联的类型, ...
- C++ Primer : 第十一章 : 关联容器之概述、有序关联容器关键字要求和pair类型
标准库定义了两种主要的关联容器:map和set map中的元素时一些关键字-值(key-value)对,关键字起到索引的作用,值则表示与索引相关的数据.set中每个元素只包含一个关键字,可以完成高效的 ...
- 使用multimap创建重复键关联容器
在“使用 <map> 库创建关联容器”一文中,我们讨论了标准库中的 map 关联容器.但那只是 map 容器的一部分.标准库还定义了一个 multimap 容器,它与 map 类似,所不同 ...
- Chapter11:关联容器
当用decltype来获得一个函数指针类型时,必须加上一个*来指出我们要使用一个给定函数类型的指针.decltype<CompareIsbn>*. map<string, int&g ...
- C++ 之关联容器 map
标准库定义了四种关联容器:map是其中之一(另外还有set.multimap.multiset).map的元素以键-值(key-value),在学了顺序容器之后,再学习关联容器,就比较比较好理解了. ...
- C++ 关联容器详解——从内部结构到应用
关联容器不同于顺序容器的是:顺序容器底层用数组实现,为线性结构:关联容器在实现中,用到的非线性存储方式: 顺序容器是通过元素在容器中的位置顺序存储和访问元素,而关联容器是通过键(key)存储和读取元素 ...
随机推荐
- 转 对菜鸟开发者的叮咛:花一万个小时练习Coding,不要浪费一万小时无谓地Debugging
原文见http://blog.jobbole.com/74825/ Coding 之于科技的重要性不言可喻,也不再是软体工程师的专利,医师.律师.会计师.护理师.金融从业人员,甚至是听起来摸不着边的政 ...
- $使用dom4j可解析 返回&#x等字样的 html转义字符
如果以GET或POST请求某个系统返回,带有 $#x 那很有可能是axis服务器返回的. <?xml version="1.0" encoding="UTF-8&q ...
- nvidia 各种卡
cudnn是针对maxwell优化的啊, maxwell下的各种卡都是游戏卡,具体可以见: https://developer.nvidia.com/maxwell-compute-architect ...
- 图解Android Studio导入Eclipse项目源码
方法/步骤 打开Android Studio,在主页面中选择"File"->"New"->"Import project...&quo ...
- Pig Latin
function translate(str) { //return str; var list = ['a','e','i','o','u']; if(list.indexOf(str[0]) &g ...
- java 抽象类与接口的区别
在Java 语言中, abstract class 和interface 是支持抽象类 定义的两种机制.正是由于这两种机制的存在,才赋予 了Java强大的 面向对象能力.abstract class和 ...
- 解决winrar压缩软件弹出广告
最近winrar每次打开压缩包就会弹出一个广告,那是因为winrar是收费软件,注册了就没有广告了.下面我教大家怎么注册来屏蔽广告. 解决方法 1.新建一个txt文件并命名为"rarreg. ...
- 谈谈Linux下动态库查找路径的问题
学习到了一个阶段之后,就需要不断的总结.沉淀.清零,然后才能继续"上路".回想起自己当年刚接触Linux时,不管是用源码包编译程序,还是程序运行时出现的和动态库的各种恩恩怨怨,心里 ...
- Odoo10尝鲜: 退货
Odoo sale / purchase 在 v9 改进之后, 开立发票的入口 不再像之前的版本,有多个来源,例如 订单 交货单 记工单 分析分录 现在只有一个入口,只需要在 订单上开票,这样 ...
- secure erase 时必须umount
不umont就进行secure erase,仍然会执行成功,但是ssd确没有被清0,使用hexdump可以验证. 但是secure erase 命令却返回成功,造成误解以为已经擦除成功. 所以,sec ...