掌握C++ map:高效键值对操作指南
map底层的红黑树节点中的数据,使用pair<Key, T>存储键值对数据。。
map是一个key_value的结构,不允许出现重复的数据,从上图中我们可以看出map由四部分组成:
- Key就是map底层关键字的类型
- T是map底层value的类型
- map默认要求Key支持小于的比较,如果不满足要求或者不满足相应的需要可以自行实现仿函数传给模板参数
- map底层存储数据的内存是从空间配置器申请的
一般情况下,我们都不需要传后两个参数,map底层是用红黑树实现的,增删插改效率是
![]()
O(logN)
,迭代器遍历走的是中序,所以是按key有序顺序遍历(map的顺序是由key决定的)
map的底层直接存储了一个key_value的结构体——pair

pair是何方神圣呢?我们一起来看一下——
2.1 pair类型介绍
map底层的红黑树节点中的数据,使用pair<Key, T>存储键值对数据。

2.2 创建 map 对象
|
explicit map (const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type()); |
无参构造,构造一个空的map对象 |
|---|---|
|
template <class InputIterator> map (InputIterator first, InputIterator last,const key_compare& comp = key_compare(), const allocator_type& = allocator_type()); |
迭代器区间构造 |
|
map (const map& x); |
拷贝构造 |
|
map (initializer_list<value_type> il; |
initializer list (5) initializer 列表构造 |
创建一个空的map对象——(最常用的方式)
代码语言:javascript
AI代码解释
void test_map1()
{
//创建一个空的map对象
//假设key的类型是string,T的类型是string/int
map<string, string> map1;
map<string, int> map2;
}

一般情况下,我们都不需要传剩余的两个参数!!!
2.3 插入键值对:insert 方法
在map中使用insert插入一个数据,插入的数据也是一个pair!!!
假设现在我们要实现一个字典,那我们该怎么插入一个pair数据呢?

ok,我们先来看看怎么构造一个pair对象——
2.3.1 创建键值对:pair 的构造
代码语言:javascript
AI代码解释
void test_map2()
{
//创建一个pair对象(有名对象)
pair<string, string> kv1("sort", "排序");
//使用匿名对象
pair<string, string>("left", "左边");
//make_pair 一个函数模板:给两个值,会根据这两个值来构造一个类型
make_pair("right", "右边");
}
ok,这样我们就可以实现插入的操作——
代码语言:javascript
AI代码解释
void test_map2()
{
map<string, string> dict;
//创建一个pair对象(有名对象)
pair<string, string> kv1("sort", "排序");
dict.insert(kv1);
//使用匿名对象
dict.insert(pair<string, string>("left", "左边"));
//make_pair 一个函数模板:给两个值,会根据这两个值来构造一个类型
dict.insert(make_pair("right", "右边"));
}
插入结果:

但是不知道有没有uu发现上面使用构造一个pair对象,匿名对象,make_pair进行插入的操作有点麻烦,那我们就可以使用下面的方式:
代码语言:javascript
AI代码解释
dict.insert({ "map","地图" });

注意:这里不是initializer_list的构造
如果是initializer_list的构造,应该是pair的initializer_list
代码语言:javascript
AI代码解释
dict.insert({ {"string","字符串"},{"begin","开始"} });//initializer_list构造
注意:key相同的数据,无法插入(map不允许key重复)!!!
2.4 遍历键值对:map的迭代器使用

- 迭代器遍历map

嗯?为什么这里会报错?
ok,这是因为这里无法直接进行打印,为什么?
*it1的结果是一个pair类型的结构体,pair不支持打印,pair中有两个成员:first和second;如果直接打印,该打印谁呢!
ok,对于这种内部有多个成员组成的结构体类型,我们可以使用下面的方法进行打印——
代码语言:javascript
AI代码解释
while (it1 != dict.end())
{
cout << (*it1).first << ":" << (*it1).second << endl;
++it1;
}
但是我们更建议使用下面的方式进行打印——
代码语言:javascript
AI代码解释
while (it1 != dict.end())
{
cout << it1->first << ":" << it1->second << endl;
++it1;
}

- 范围for遍历
方式一:

方式二:(结构化绑定C++17支持)最推荐使用

对于这种方式我们可以这么理解:

代码语言:javascript
AI代码解释
void test_map2()
{
map<string, string> dict;
//创建一个pair对象(有名对象)
pair<string, string> kv1("sort", "排序");
dict.insert(kv1);
//使用匿名对象
dict.insert(pair<string, string>("left", "左边"));
//make_pair 一个函数模板:给两个值,会根据这两个值来构造一个类型
dict.insert(make_pair("right", "右边"));
//隐式类型转换
dict.insert({ "map","地图" });
//initiaizer_list(pair的initiaizer_list)
dict.insert({ {"string","字符串"},{"begin","开始"} });
//begin
map<string, string>::iterator it1 = dict.begin();
//end
map<string, string>::iterator it2 = dict.end();
//遍历方式一:
while (it1 != dict.end())
{
cout << (*it1).first << ":" << (*it1).second << endl;
++it1;
}
//遍历方式二:(相较于1更推荐2)
while (it1 != dict.end())
{
cout << it1->first << ":" << it1->second << endl;
++it1;
}
//方式三
for (auto& e : dict)//*iterator是一个pair
{
cout << e.first << ":" << e.second << endl;
}
//方式四(最推荐使用)
for (auto& [k, v] : dict)
{
cout << k << ":" << v << endl;
}
}
注意:遍历的结果按key值升序排列(从小到大)!!!
2.5 删除键值对:erase 操作
|
iterator erase (const_iterator position); |
删除指定迭代器位置上的数据 |
|---|---|
|
size_type erase (const key_type& k); |
删除指定key数据(删除只看key,不看value),成功删除返回1,失败返回0 |
|
iterator erase (const_iterator first, const_iterator last); |
删除[first,last)区间的数据 |
代码语言:javascript
AI代码解释
map<string, string>::iterator it1 = dict.begin();
//删除指定迭代器位置上的数据
dict.erase(it1);
//删除指定key的数据(存在就删,否则什么都不做)
dict.erase("map");
//删除一段迭代器区间的数据
map<string, string>::iterator it2 = dict.begin();
dict.erase(++it2, dict.end());
注意:erase会有迭代器失效!!!
2.6 查找键值对:find 操作
|
iterator find (const key_type& k); |
查找key,查找到返回key所在的迭代器;没有查找到,返回end迭代器 |
|---|---|
|
const_iterator find (const key_type& k) const; |
查找const key,查找到返回const key所在的const 迭代器;没有查找到,返回end迭代器 |
代码语言:javascript
AI代码解释
void test_map3()
{
map<string, string> dict;
dict.insert({ "sort","排序" });
dict.insert({ "string","字符串" });
dict.insert({ "map","地图" });
dict.insert({ "left","左边" });
dict.insert({ "right","右边" });
dict.insert({ "begin","开始" });
//查找
auto ret = dict.find("right");
if (ret != dict.end())
{
cout << "找到了";
}
else
cout << "没有该单词";
}
运行结果:

2.7 更新键对应的值
通过前面的学习,我们知道set是不能进行修改操作的,但是map可以进行修改操作:通过key来修改value

运行结果:

ok,那除了上面这种修改的方式还有没有其他更便捷,更牛逼的修改方式呢?
肯定是有的
2.7.1 map中的operator[ ] (重点)
在map中重载了" [ ] " 运算符,我们可以通过它来进行修改操作
代码语言:javascript
AI代码解释
void test_map4()
{
string arr[]= { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
map<string, int> countmap;
for (auto& e : arr)
{
countmap[e]++;
}
for (auto& [k, v]:countmap)
{
cout << k << ":" << v << endl;
}
}
运行结果:

这到底是咋做到了,感觉有点小牛逼啊!!!
map::operator[ ] 的意思是 你给我key,我找到对应的value

ok,这里还有一个问题:
如果这个key是第一次出现怎么办?这个key都不在map中怎么办呢?
countmap[ ](e)++;
- key已经在map中,返回value的引用,然后++,没有任何问题;
- key不在map中,第一次出现在map中呢?怎么办?
这就说明[ ] 还有他的新功能!!!
更多推荐

所有评论(0)