本文主要内容
1.理解键值对
2.map 的使用
3.multimap 的使用
4.底层结构
1.键值对
键值对是用来表示一对一的数据映射关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表关键字,value代表与key对应的信息值。
我们生活中有许多这样的对应关系,例如英汉互译字典就是典型的<k,v>模型。每个中文词语与英文单词就存在着一一映射的关系,我们用其他数据结构描述出这样的关系可能需要很多辅助,但是用map就能轻易的进行描述。这就是键值对为我们**在用编程处理一对一数据的时候,提供的快速通道。**下面是键值对的定义:
STL中关于键值对的定义:
//键值对
template<class K,class V>
struct pair
{
typedef K first_type;
typedef V second_type;
K first;
V second;
pair()
:first(K())
, second(V())
{}
pair(const K& a, const V&b)
: first(a)
, second(b)
{}
}
make_pair ( , )的定义:
//make_pair 是函数模板,根据pair的类型直接推出参数类型
template<class K, class V>
pair<K, V> make_pair(const K&k, const V&v)
{
return pair<K, V>(k, v);
}
2. map的使用
想要深入了解的可以查看:map文档
下面我简单介绍 一下map:
- map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元素。
- 在map中,键值key通常用于排序和惟一地标识元素,而值value中存储与此键值key关联的内容。键值 key和值value的类型可能不同,并且在map的内部,key与value通过成员类型value_type绑定在一起, 为其取别名称为pair: typedef pair value_type;
- 在内部,map中的元素总是按照键值key进行比较排序的。
- map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序对元素进行 直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)。
- map支持下标访问符,即在[]中放入key,就可以找到与key对应的value。
- map通常是用红黑树实现的。
map使用示例–英汉词典
map提供一对一的数据处理能力(其中第一个是关键字,第二个是该关键字对应的值),由于这个特性(键值对),我们在描述英汉互译词典时就变得很轻松了。下面是使用map描述字典的示例
示例代码
//map 的使用--英汉互译字典
void test_map()
{
map<string, string> m1;
map<string, string>m2{ { "apple", "苹果" },
{ "banan", "香蕉" }, { "orange", "橘子" } };
cout << m2["apple"] << endl; //苹果
cout << m2["peach"] << endl; //空串
//迭代器使用
map<string, string>::iterator it = m2.begin();
//auto it=m2.begin();
while (it != m2.end())
{
cout << (*it).first << ":" << (*it).second << " ";
it++;
}
cout << endl;
}
map 的operator[ ]使用示例
- 接口介绍:
mapped_type&operator[ ](const key_type &k )
功能:直接返回key对应的value值。
示例:m[k]=2; 如果k的value 不存在就插入,若已经存在则修改。
operator[]的原理是:
用<key, T()>构造一个键值对,然后调用insert()函数将该键值对插入到map中
如果key已经存在,插入失败,insert函数返回该key所在位置的迭代器
如果key不存在,插入成功,insert函数返回新插入元素所在位置的迭代器
operator[]函数后 将insert返回值键值对中的value返回
注意: 在元素访问时,有一个与operator[]类似的操作at()函数(该函数不常用),都是通过key找到与key 对应的value然后返回其引用,不同的是:当key不存在时,operator[]用默认的value与key构造键值对然后插入后返回该默认value,at()函数直接抛异常。
示例代码
void TestMap() {
map<string, string> m; // 构造一个空的map,此时m中一个元素都没有
m ["apple"] = "苹果";
cout << m["apple"] << endl; //返回value值
cout << m["banan"] << endl; //插入key
cout << m.size() << endl;
m.at("peach"); //直接抛出错误
}
map 中可修改元素的接口使用示例
- pair<iterator,bool> insert(const value_type&x)
功能:在map中插入键值对x,返回值的键值对:iterator 表示插入位置,bool表明是否插入成功。这里因为能直接返回位置,所以可进行修改。 - iterator insert (iterator position,const value_type&x)
功能:在当前位置插入值为X的键值对,返回该键值对在map中的位置。 - iterator find(const key_type &x)
功能:在map查找key为x的元素,找到就返回该元素位置的迭代器,否则返回end。 - size_type size()const
功能:返回map中有效元素的个数。 - size_type erase(const key_type &x)
功能:删除键值为x的元素。 - size_type count(const key_type& x)const
功能:返回key为x的键值对在map 中的个数,因为map中的key是唯一的,因此返回值只能是1或0。因此可以用它检测key是否存在map中。
示例代码
void TestMap2()
{
map<string, string> m;
//用不同的方法再map中插入元素
m.insert(pair<string, string>("peach", "桃子")); //用pair直接来构造键值对
m.insert(make_pair("banan", "香蕉")); //用make_pair函数来构造键值对
m["apple"] = "苹果"; // 借用operator[]向map中插入元素
m.insert(m.find("banan"), make_pair("waterme", "水蜜桃")); //iterator的insert中的position 只是参考位置
cout << m.size() << endl;
// 用迭代器去遍历map中的元素,可以得到一个按照key排序的序列
for (const auto& e : m)
cout << e.first << ":" << e.second << endl;
cout << endl;
// map中的键值对key一定是唯一的,如果key存在将插入失败
auto ret = m.insert(make_pair("peach", "桃色"));
if (ret.second)
cout << "<peach, 桃色>不在map中, 刚插入" << endl;
else
cout << "key为peach的元素已经存在,如下:" << ret.first->first << ":" << ret.first->second << endl;
// 删除key为"apple"的元素
m.erase("apple");
if (m.count("apple")) //count 检测key在map中的个数,只能是1或0
cout << "apple还在" << endl;
else
cout << "apple被删除" << endl;
cout << "剩下的水果有 ->" << endl;
for (auto& e : m)
cout << e.first << ":" << e.second << endl;
}
map 小结
- map 中的元素是键值对,即<k,v>一对一的数据处理
- map 中的key是唯一的,并且不能修改(value可以变化)
- map 中的元素默认按照小于的方式对key进行比较,如果用迭代器去遍历,可以得到一个有序序列(即默认升序)
- map 的底层结构是一颗自建的平衡二叉搜索树即红黑树,所以查找效率更高
- map 支持[ ]操作符,operator[ ]中实际先进行了查找返回value值,若查找失败就插入(返回value值是默认的)
3. multimap的使用
想要深入了解的伙伴可以查看:multimap文档
下面我就简单介绍一下multimap:
- Multimaps是关联式容器,它按照特定的顺序,存储由key和value映射成的键值对<key, value>,其中 多个键值对之间的key是可以重复的。
- 在multimap中,通常按照key排序和惟一地标识元素,而映射的value存储与key关联的内容。key和 value的类型可能不同,通过multimap内部的成员类型value_type组合在一起,value_type是组合key 和value的键值对: typedef pair<const Key, T> value_type;
- 在内部,multimap中的元素总是通过其内部比较对象,按照指定的特定严格弱排序标准对key进行排序 的。
- multimap通过key访问单个元素的速度通常比unordered_multimap容器慢,但是使用迭代器直接遍历 multimap中的元素可以得到关于key有序的序列。
- multimap在底层用红黑树来实现的。
multimap中接口使用与map的使用几乎无异。但有以下几点不同处需要注意:
- map中的key是唯一的,而multimap中key是可以重复插入的
- multimap中的元素也会默认将key按照小于来比较(相同的key值也能输出)
- multimap中没有重载operator[]操作(因为key值是可重复的,底层不知道该修改哪个key的value)
示例代码1
void TestMultimap1()
{
multimap<string, string> m; //构造一个空的multimap
m.insert(make_pair("李逵", "黑旋风"));
m.insert(make_pair("林冲", "豹子头"));
m.insert(make_pair("鲁达", "花和尚"));
m.insert(make_pair("李逵", "铁牛")); //尝试插入相同key的不同
cout << m.count("李逵") << endl; // 统计key为李逵的元素有多少个
for (auto& e : m)
cout << "<" << e.first << ":" << e.second << endl; //表示插入成功
}
示例代码2
void TestMultimap2()
{
multimap<int, int> m;
for (int i = 0; i < 10; ++i)
m.insert(pair<int, int>(i, i));
for (auto& e : m)
cout << e.first << ":" << e.second << endl;
cout << endl;
// 返回m中大于等于5的第一个元素
auto it = m.lower_bound(5);
cout << it->first << ":" << it->second << endl;
// 返回m中大于5的第一个元素
it = m.upper_bound(5);
cout << it->first << ":" << it->second << endl;
}
底层结构
STL实现有两种不同结构的数据结构:树形结构和哈希结构。
- 树形结构的关联式容器主要有四个:map,set,multimap,multiset。这四种容器的共同特点是使用平衡搜索树(即红黑树)作为底层结构,容器中的元素是一个有序的序列。因此红黑树的实现作为重要内容,可参考红黑树的实现原理这篇博客进行理解
- 哈希结构的关联式容器主要有两个:unordered_map ,unordered_set。 这两种容器的共同特点是使用哈希列表作为底层结构,容器中的元素是一个无序的序列。因此解决哈希冲突也成为了一个重要内容,可参考哈希冲突解决方法这篇博客进行理解
其他内容
练习:
1.用map 的接口实现< k,v >模型中以key为关键字的排序map 实现对 <k,v> 模型的排序
2.用map 的接口实现< k,v >模型中以value为关键字的排序58同城秋招笔试题解答
扩展:
STL进阶之树形结构的关联式容器—— set和multiset