之前几篇文章讨论的C++语言中的 STL 模板容器类存放的都是单一类型的数据:
本文将介绍C++语言中的 STL 模板容器类:map(映射)
,它是一个关联容器类,内部存放的是 key-value
键值对,其中的“key(键)”总是独一无二的,可以从容器中删除它,也可以把新的key-value
插入到容器中,但是一旦插入,key 本身就无法修改了,其对应的 value 倒是可以修改,下面将通过实例看到这一点。
声明和初始化
C++语言程序开发中,要使用 map 容器,需要包含<map>
头文件:
#include <map>
map 容器中的 key-value 的数据类型可以是任意的,定义一个 map 对象的C++语言代码可以如下写:
std::map<key_type, value_type> val;
map在标准命名空间
std
中,下文为了方便,实例代码不再写std::
。
其中的 key_type
和 value_type
分别表示 key-value
中的 key 的数据类型和 value 的数据类型,例如:
map <int, int> mymap;
这表示 map 对象 mymap 容器中存放的将是 int-int 类型的数据,在C++11标准的支持下,可以在定义 map 对象的同时赋初值,例如:
map<int, int> mymap {{1, 10}, {2, 20}, {3, 30}};
上面这行C++语言代码执行后,mymap 容器中将有 3 个键值对。应该注意,在往 map 容器中插入元素时,必须以 key-value 的形式,而不能只插入 key 或者只插入 value。
操作 map
C++语言中的 map 容器类的基本操作同前面介绍的几个容器类大体相同,常用的包括以下几个操作(或者说成员函数):
- at 和 []:用于访问 map 容器中的元素,at 和 [] 的功能基本相同,只有一个区别:如果访问 map 容器中不存在的元素,at 将抛出一个异常,而 [] 操作符则将这个“不存在的元素”插入到 map 容器中。
- begin:返回 map 容器中第一个元素的迭代器指针。
- end:返回 map 容器末尾位置(最后一个元素的下一个位置)的迭代器指针。
下面是一段C++语言代码示例,该示例主要使用了前文提到的点,请看:
#include <iostream>
#include <map>
using namespace std;
int main()
{
map<int, int> mymap {{1,10},{2,20},{3,30}};
// 遍历 mymap
map<int,int> :: iterator it;
cout << "\nThe map mymap is : \n";
cout << "\tKEY\tVALUE\n";
for (it = mymap.begin(); it != mymap.end(); ++it) {
cout << '\t' << it->first
<< '\t' << it->second << '\n';
}
cout << endl;
mymap.at(2) = 10; // 修改 key(2) 对应的值
mymap[3] = 10; // 修改 key(3) 对应的值
//遍历修改后的 mymap
cout << "\nThe map mymap is : \n";
cout << "\tKEY\tVALUE\n";
for (it = mymap.begin(); it != mymap.end(); ++it) {
cout << '\t' << it->first
<< '\t' << it->second << '\n';
}
cout << endl;
return 0;
}
上面这段C++语言代码很简单:先定义了一个 map 对象 mymap,并且初始化了 3 个键值对,然后将之打印出来。在打印过程中可以看出,迭代器指针的 first
成员对应着 key,而 second
成员则对应着 value。之后,通过 at 和 [] 操作符分别将 key(2) 和 key(3) 对应的值修改为 10,再打印出修改后的 mymap。编译这段C++语言代码时注意指定-std=c++11
,输出如下:
# g++ t1.cpp -std=c++11
# ./a.out
The map mymap is :
KEY VALUE
1 10
2 20
3 30
The map mymap is :
KEY VALUE
1 10
2 10
一切与预期一致。当然了,map 容器类比较常用的操作方法还有一些:
- insert(key, value):将键值对(key, value)插入到 map 容器中。
-
erase:从 map 容器中删除元素,该方法有两个版本:
- erase(pos) - 删除 map 容器中指定位置 pos 处的元素。
- erase(key) - 删除 key 对应的元素。
- empty:检查 map 容器是否没有元素。
- size:返回 map 容器中元素的数目。
- max_size:返回 map 容器能够容纳的最多元素数目。
- clear:删除 map 容器中的所有元素。
- count:返回容器中给定 key 对应的元素数目。
下面是一段C++语言代码示例,请看:
#include <iostream>
#include <map>
using namespace std;
int main()
{
map<int,int> mymap;
mymap.insert(pair<int, int>(1, 1));
mymap.insert(pair<int, int>(2, 3));
mymap.insert(pair<int, int>(3, 5));
mymap.insert(pair<int, int>(4, 7));
mymap.insert(pair<int, int>(5, 9));
mymap.insert(pair<int, int>(6, 11));
mymap.insert(pair<int, int>(7, 13));
map<int,int> :: iterator it;
cout << "\nThe map mymap is : \n";
cout << "\tKEY\tVALUE\n";
for (it = mymap.begin(); it != mymap.end(); ++it) {
cout << '\t' << it->first
<< '\t' << it->second << '\n';
}
cout << endl;
cout<<"\n mymap.size(): "<<mymap.size();
cout<<"\n mymap.max_size(): "<<mymap.max_size();
cout<<endl;
int num;
num = mymap.erase(3);
cout << "\nmymap.erase(3) : ";
cout << num << " removed \n";
cout << "\tKEY\tELEMENT\n";
for (it = mymap.begin(); it != mymap.end(); ++it) {
cout << '\t' << it->first
<< '\t' << it->second << '\n';
}
cout << endl;
mymap.clear();
cout<<"\nAfter clear operation mymap.size() : "<<mymap.size() <<endl;
return 0;
}
上面的C++语言代码首先使用 insert() 函数插入了 7 组键值对,并且打印了它们,接着通过 size() 和 max_size() 函数分别输出了当前容器 mymap 中的元素数目,和能够容纳的最多数目(这主要取决于机器内存)。
接着,代码调用了 erase(3) 函数删除了 key(3) 对应的元素,并打印了出来。最后代码调用了 clear() 函数删除了容器中所有的元素,此时 size() 函数将返回 0。
同样的,编译这段C++语言代码需要指定-std=c++11
选项,最终输出如下,请看:
# g++ t2.cpp -std=c++11
# ./a.out
The map mymap is :
KEY VALUE
1 1
2 3
3 5
4 7
5 9
6 11
7 13
mymap.size(): 7
mymap.max_size(): 461168601842738790
mymap.erase(3) : 1 removed
KEY ELEMENT
1 1
2 3
4 7
5 9
6 11
7 13
After clear operation mymap.size() : 0
多重映射 multimap
multimap 和 map 在绝大多数方面都是相似的,比较明显的区别是 multimap 允许多个元素拥有相同的 key,所以,multimap 中的元素的 key 可以不是独一无二的,不过这样一来,就不能再通过 at 和 [] 方法访问元素了。
操作 multimap 容器类的方法与操作 map 容器类的方法大体相同,下面是一段C++代码示例:
#include <iostream>
#include <map>
using namespace std;
int main()
{
multimap<int,int> mymap;
mymap.insert(pair<int, int>(1, 1));
mymap.insert(pair<int, int>(2, 3));
mymap.insert(pair<int, int>(3, 5));
mymap.insert(pair<int, int>(3, 7));
mymap.insert(pair<int, int>(4, 9));
map<int,int> :: iterator it;
cout << "\nThe multimap mymap is : \n";
cout << "\tKEY\tVALUE\n";
for (it = mymap.begin(); it != mymap.end(); ++it) {
cout << '\t' << it->first
<< '\t' << it->second << '\n';
}
cout << endl;
cout<<"\n mymap.size(): "<<mymap.size();
cout<<"\n mymap.max_size(): "<<mymap.max_size();
cout<<endl;
int num;
num = mymap.erase(3);
cout << "\nmymap.erase(3) : ";
cout << num << " removed \n";
cout << "\tKEY\tELEMENT\n";
for (it = mymap.begin(); it != mymap.end(); ++it) {
cout << '\t' << it->first
<< '\t' << it->second << '\n';
}
cout << endl;
mymap.clear();
cout<<"\nAfter clear operation mymap.size() : "<<mymap.size();
return 0;
}
这段C++语言代码与前面操作 map 容器类的代码几乎相同,编译这段代码并执行,得到如下输出:
# g++ t3.cpp -std=c++11
# ./a.out
The multimap mymap is :
KEY VALUE
1 1
2 3
3 5
3 7
4 9
mymap.size(): 5
mymap.max_size(): 461168601842738790
mymap.erase(3) : 2 removed
KEY ELEMENT
1 1
2 3
4 9
After clear operation mymap.size() : 0
我们将注意力放在 key(3) 上,从输出来看,multimap 容器类的确允许同一个 key 被多个元素使用,但是也要注意,因为不能保证唯一性,multimap 容器类没有 at 和 [] 方法。
无序映射 unordered_map
和普通的 map 一样,C++语言中的 unordered_map 类也是一种存储 key-value 对的容器。只不过,unordered_map 中的元素可以是无序的。
与普通的 map 一样,unordered_map 容器中的 key 也是用于映射 value 的,相关的 key-value 类型也可以是任意的。
unordered_map 容器类内部的无序映射的实现是由“哈希表”数据结构提供的,key 对应着哈希表中的索引,因此,unordered_map 容器类的性能在很大程度上取决于所提供的哈希函数。
使用 unordered_map 容器类需要包含头文件 <unordered_map>
,实例化一个对象和普通 map 没有什么区别:
#include <unordered_map>
std::unordered_map<key_type, value_type> val;
因为 unordered_map 容器内部使用的是哈希表数据结构,所以相比于普通 map 容器类多出了几个方法:
- bucket:返回容器中 key 对应元素的 bucket 数目。
- bucket_count:返回容器中所有的 bucket 数目。
- bucket_size:返回每个 bucket 中的元素数目。
关于
bucket
,之后的文章会讨论。
下面是一段C++语言代码示例,请看:
#include <iostream>
#include <unordered_map>
using namespace std;
int main()
{
unordered_map<string, int> umap;
// 使用 [] 操作符插入元素
umap["RED"] = 1;
umap["GREEN"] = 2;
umap["BLUE"] = 3;
// 使用 insert() 函数插入元素
umap.insert(make_pair("YELLOW", 4));
unordered_map<string, int>:: iterator it;
cout << "\nAll Elements in unordered map umap : \n";
for (it = umap.begin(); it != umap.end(); ++it)
{
cout << it->first << "\t" << it->second << endl;
}
cout<<endl;
cout<<"bucket of RED: "<<umap.bucket("RED") <<endl;
cout<<"umap bucket_count : "<<umap.bucket_count()<<endl;
cout<<"size of first bucket: "<<umap.bucket_size(1)<<endl;
return 0;
}
上述C++语言代码的前半段和操作普通的 map 容器类没什么区别,后半段主要打印了几个 bucket 相关的值。编译这段代码并执行,得到如下输出:
# g++ t4.cpp -std=c++11
# ./a.out
All Elements in unordered map umap :
YELLOW 4
BLUE 3
GREEN 2
RED 1
bucket of RED: 1
umap bucket_count : 11
size of first bucket: 1
小结
本文主要讨论了C++语言中的标准容器类map
及其相关的两个变种类multimap
和unordered_map
的基本使用,其内部实现倒是浅尝辄止了。不过,学会使用它们对理解它们是有所帮助的,熟练后再去理解相关的详细设计和实现,绝对是事半功倍的。