详解C++STL容器系列(三)—— map属性和方法详解

  • Post author:
  • Post category:其他




一、map介绍

  • std::map是一种关联容器,即以

    key-value

    键值对的形式存储数据。
  • map底层实现是

    红黑树

    ,而C++11新增容器unordered_map与map用法基本一致,但底层是:

    哈希表

  • 红黑树是一种平衡二叉树,而平衡二叉树在二叉排序树基础上的,所以红黑树也就有排序的特性(unordered_map没有排序)。可以做到在O(log n)时间内完成查找,插入和删除,在对单次时间敏感的场景下比较建议使用map做为容器。



二、map的属性和方法



iterators

名字 描述
begin 返回指向容器中第一个(经过排序后)键值对的双向迭代器。
end 返回指向容器最后一个元素(排序后)所在位置后一个位置的键值对的双向迭代器
rbegin 返回容器逆序的第一个键值对的双向迭代器
rend 返回容器逆序的最后一个元素的前一个位置的键值对双向迭代器
cbegin 和begin()功能相同,在其基础上增加了 const 属性,不能用于修改元素。
cend 和end()功能相同,在其基础上增加了 const 属性,不能用于修改元素。
crbegin 和rbegin()功能相同,在其基础上增加了 const 属性,不能用于修改元素。
crend 和rend()功能相同,在其基础上增加了 const 属性,不能用于修改元素。



capacity

名字 描述
size 返回存在键值对的个数
max_size 返回元素个数的最大值。这个值非常大,一般是2^32-1
empty 判断容器是否为空,为空返回true否则false



Element access

名字 描述
operator[] map容器重载了 [] 运算符,只要知道 map 容器中某个键值对的键的值,就可以向获取数组中元素那样,通过键直接获取对应的值。
at 找到 map 容器中 key 键对应的值,如果找不到,该函数会引发 out_of_range 异常。



Modifiers

名字 描述
insert 向map中插入键值对
erase 删除map容器键值或者键值对
swap 交换2个map容器中的所有键值对
clear 清除map容器中的所有键值对
emplace 在当前 map 容器中的指定位置处构造新键值对。其效果和插入键值对一样,但效率更高。
emplace_hint 在本质上和 emplace() 在 map 容器中构造新键值对的方式是一样的,不同之处在于,使用者必须为该方法提供一个指示键值对生成位置的迭代器,并作为该方法的第一个参数。



Operations

名字 描述
find 在 map 容器中查找键为 key 的键值对,如果成功找到,则返回指向该键值对的双向迭代器;反之,则返回和 end() 方法一样的迭代器。另外,如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
count 在当前 map 容器中,查找键为 key 的键值对的个数并返回。注意,由于 map 容器中各键值对的键的值是唯一的,因此该函数的返回值最大为 1。
lower_bound 返回一个指向当前 map 容器中第一个大于或等于 key 的键值对的双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
upper_bound 返回一个指向当前 map 容器中第一个大于 key 的键值对的迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
equal_range 该方法返回一个 pair 对象(包含 2 个双向迭代器),其中 pair.first 和 lower_bound() 方法的返回值等价,pair.second 和 upper_bound() 方法的返回值等价。也就是说,该方法将返回一个范围,该范围中包含的键为 key 的键值对(map 容器键值对唯一,因此该范围最多包含一个键值对)。



三、map的具体用法



3.1 iterator(迭代器访问)

map<string, int> myMap;
myMap["张三"] = 20;
myMap["李四"] = 22;
myMap["孙钱"] = 18;
for (map<string, int>::iterator it = myMap.begin(); it != myMap.end(); it++)
{
    cout << it->first << " :" << it->second << endl;
}
/* 输出
李四 :22
孙钱 :18
张三 :20
*/

由以上代码可以看到,map会对元素进行排序,”李四”开头字母

L

、”孙钱”开头字母

S

、”张三”开头字母

Z



L < S < Z

,可见map默认以key值从小到大排序。

map<string, int> myMap;
myMap["张三"] = 20;
myMap["李四"] = 22;
myMap["孙钱"] = 18;
for (auto it = myMap.rbegin(); it != myMap.rend(); it++)
{
    cout << it->first << " :" << it->second << endl;
}

rbegin()和rend()则可从尾到头遍历。



3.2 capacity(容量)


  • size()

    返回map中键值对的个数

  • max_size()

    map中能存储的最大值

  • empty()

    判断map是否为空:为空返回true,不为空返回false
map<string, int> myMap;
myMap["张三"] = 20;
myMap["李四"] = 22;
myMap["孙钱"] = 18;
cout << myMap.size() << endl;//大小为3
cout << myMap.max_size() << endl;//最大容量:89478485
cout << myMap.empty() << endl;//非空即为0



3.3 Element access(下标访问)

  • map重载了

    []

    运算符,可以通过map[]通过key值访问其value。
  • at作用和[]用法一致,实现效果一致。
  • 如果某个key值不存在,即没有该键值对,返回0。(可见各个位置初始值为0)。
map<string, int> myMap;
myMap["张三"] = 20;
myMap["李四"] = 22;
myMap["孙钱"] = 18;
cout << myMap["张三"] << endl;//20
cout << myMap.at("张三") << endl;//20
cout << myMap["王五"] << endl;//0



3.4 Modifiers(修改器)

这部分主要用于修改元素,如

增删改查

等操作。



3.4.1 insert

因为map中存储的元素时键值对类型(pair),所以insert可以有两种插入方式:

(1)使用pair、value_type、make_pair()函数,插入一个键值对

map<string, int> myMap;
myMap.insert(pair<string, int>("李四", 22));
myMap.insert(map<string, int>::value_type("张三", 20));
pair<map<string, int>::iterator, bool> ans;//定义返回值类型
ans = myMap.insert(make_pair("孙钱", 18));
cout << ans.first->first << ":" << ans.first->second << endl;
myMap.insert(myMap.begin(), make_pair("赵六", 28));

注意,插入后的返回值是一个pair类型,pair.first是map的迭代器,pair.second为是否插入成功的返回值。

(2)在指定位置插入元素

map<string, int> myMap;
myMap.insert(pair<string, int>("李四", 22));
myMap.insert(map<string, int>::value_type("张三", 20));
myMap.insert(make_pair("孙钱", 18));
myMap.insert(myMap.begin(), make_pair("赵六", 28));//在map头部后面插入
for (auto it = myMap.begin(); it != myMap.end(); it++)
{
	cout << it->first << ":" << it->second << endl;
}

但是通过显示的结果可知,即使指定在某个位置后插入,但是实际上map还是会根据

key值

将其排序。

注意,和前一种用法不一样的是,

返回值是迭代器

,而不是pair。

(3)在某个范围内插入元素,[first, last]

map<string, int> myMap;
myMap.insert(pair<string, int>("李四", 22));
myMap.insert(map<string, int>::value_type("张三", 20));
myMap.insert(make_pair("孙钱", 18));

map<string, int> newMap;
newMap.insert(myMap.begin(), myMap.end());
for (auto it : newMap)
cout << it.first << ":" << it.second << endl;

(4)一次性插入多个键值对

map<string, int> myMap;
myMap.insert({ {"李四", 22}, {"张三", 20} });//一次性插入两个键值对
myMap.insert(make_pair("孙钱", 18));



3.4.2 erase

删除元素,输入参数为迭代器,有以下两种遍历删除元素的方法:

(1)通过迭代器删除元素

  • 因为erase的返回值是下一个迭代器的指针,所以可以用返回值做下一次删除的元素

    map<string, int> myMap;
    myMap.insert(map<string, int>::value_type("张三", 20));
    myMap.insert(map<string, int>::value_type("李四", 22));
    myMap.insert(map<string, int>::value_type("孙钱", 18));
    for (auto it = myMap.begin(); it != myMap.end(); )
    {
    it = myMap.erase(it);
    }
    cout << myMap.empty() << endl;
    
  • 根据后

    ++

    的特点,先删除后it偏移。

    map<string, int> myMap;
    myMap.insert(map<string, int>::value_type("张三", 20));
    myMap.insert(map<string, int>::value_type("李四", 22));
    myMap.insert(map<string, int>::value_type("孙钱", 18));
    for (auto it = myMap.begin(); it != myMap.end(); )
    {
    	myMap.erase(it++);
    }
    cout << myMap.empty() << endl;
    

(2)通过key值删除元素

map<string, int> myMap;
myMap.insert({ {"李四", 22}, {"张三", 20} });
myMap.emplace_hint(myMap.begin(), make_pair("孙钱", 18));
myMap.erase("李四");//删除key值为李四的元素



3.4.3 swap

swap交换两个map的内容,两个map的所有内容均交换。

map<string, int> myMap;
myMap.insert(map<string, int>::value_type("张三", 20));
myMap.insert(map<string, int>::value_type("李四", 22));
myMap.insert(map<string, int>::value_type("孙钱", 18));
map<string, int> myMap2;
myMap2["王五"] = 30;
myMap2["赵六"] = 50;
myMap.swap(myMap2);
for (auto it = myMap.begin(); it != myMap.end(); it++)
{
cout << it->first << ":" << it->second << endl;
}



3.4.4 clear

clear清空map的键值对,size = 0。

map<string, int> myMap;
myMap.insert(map<string, int>::value_type("张三", 20));
myMap.insert(map<string, int>::value_type("李四", 22));
myMap.insert(map<string, int>::value_type("孙钱", 18));
myMap.clear();
cout << myMap.empty() << endl;



3.4.5 emplace

用法和insert一样,只是内部实现机制不同,emplace更快,但是只能插入一个键值对。

emplace的插入可以,将创建新键值对所需的数据作为参数直接传入即可,此方法可以自行利用这些数据构建出指定的键值对。

map<string, int> myMap;
myMap.emplace(map<string, int>::value_type("张三", 20));//法1
myMap.emplace(pair<string, int>("李四", 22));//法2
myMap.emplace("赵六", 30);



3.4.6 emplace_hint

emplace_hint有点类似于insert的第二种用法,在指定位置插入。

但是,emplace_hint和emplace一样,可以将数据参数直接输入。

第一个参数是要插入的位置,接下来第二、三个参数分别是key 和 value。

map<string, int> myMap;
myMap.insert({ {"李四", 22}, {"张三", 20} });
myMap.emplace_hint(myMap.begin(), "孙钱", 18);

当然,和insert一样,指定位置插入,但是map仍然会根据key值大小进行排序。



3.5 Operations(操作)

这部分操作用于查找元素,计算元素大小。



3.5.1 find

map有find方法,需和algorithm库的find区别。map的find方法寻找键值对中的键值,返回一个迭代器。如果迭代器不是

map.end()

,那么说明找到元素,返回该元素的迭代器。

map<string, int> myMap;
myMap.insert({ {"李四", 22}, {"张三", 20} });
myMap.emplace_hint(myMap.begin(), make_pair("孙钱", 18));
auto it = myMap.find("李四");//寻找是否有名为"赵四"的人
if (it != myMap.end())
{
cout << it->first << ":" << it->second << endl;
}



3.5.2 count

map和set两种容器的底层结构都是

红黑树

,所以容器中不会出现相同的元素,因此count()的结果只能为0和1。

所以可以用

count

判断map是否有某键值对。

map<string, int> myMap;
myMap.insert({ {"李四", 22}, {"张三", 20} });
myMap.emplace_hint(myMap.begin(), make_pair("孙钱", 18));
cout << myMap.count("张三") << endl;//输出1



3.5.3 lower_bound

在STL里有lower_bound函数,用于在有序容器中找到第一个大于val值的迭代器。

同样的,在map中有该方法,返回

大于或等于

某个键值

key

的迭代器。

map<int, int> myMap;
myMap[1] = 2;
myMap[2] = 4;
myMap[3] = 7;
myMap[5] = 8;
myMap[6] = 9;
auto it = myMap.lower_bound(3);
if (it != myMap.end())
{
    cout << it->first << ":" << it->second << endl;//3:7
}



3.5.4 upper_bound

和lower_bound类似,不过upper_bound返回的是

大于

某个键值

key

的迭代器。

map<int, int> myMap;
myMap[1] = 2;
myMap[2] = 4;
myMap[3] = 7;
myMap[5] = 8;
myMap[6] = 9;
auto it = myMap.upper_bound(3);
if (it != myMap.end())
{
    cout << it->first << ":" << it->second << endl;//5:8
}



3.5.5 equal_range

pair<const_iterator,const_iterator> equal_range (const key_type& k) const;
pair<iterator,iterator>             equal_range (const key_type& k);

如上是map中equal_range的原形,返回值是一个pair类型,pair类型里是两个迭代器,pair::first返回的值等于lower_bound的返回值,即大于等于某个值的第一个值的迭代器;pair::second返回的值等于upper_bound返回的值,即大于某个值的第一个值的迭代器。

所以,equal_range用于计算某个值的所有插入的元素。因为我们可能插入(insert)了很多元素,但是实际上只有一个值有效,也就是第一个插入的值。equal_range就可以为

multimap

计算所有插入的元素。



map


不论传入多少个都只有一个值

,equal_range得到也只有一个值。

以下是multimap的

equal_range

multimap<int, int> myMap;
myMap.insert(make_pair(1, 5));
myMap.insert(make_pair(2, 4));
myMap.insert(make_pair(3, 6));
myMap.insert(make_pair(3, 5));
myMap.insert(make_pair(3, 4));


auto ret = myMap.equal_range(3);
for (auto it = ret.first; it != ret.second; it++)
{
	cout << it->first << ":" << it->second << endl;
}
//输出:
/*
3:6
3:5
3:4
*/



参考



版权声明:本文为aruewds原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。