说明
std::map
是一个有序关联容器,包含具有唯一键的键值对key-value
。键使用比较函数Compare
比较来进行排序。
搜索,删除和插入操作具有对数复杂性。map通常实现为红黑树。
定义于头文件 <map>
特别注意,map中同一个键值只能存在一个实体
map
的特点是增加和删除节点对迭代器的影响很小,除了那个被操作的节点,对其他的节点都没有什么影响(只是内部的一些指针指向的改变)。对于map
的迭代器来说,可以修改实value
,而不能修改key
。
内部构造(基本操作函数和成员)
1、成员类型
成员类型 | Definition |
---|---|
key_type | Key |
mapped_type | T |
value_type | std::pair<const Key, T> |
size_type | 无符号整数类型(通常是 std::size_t ) |
difference_type | 有符号整数类型(通常是 std::ptrdiff_t ) |
key_compare | Compare |
allocator_type | Allocator |
reference | Allocator::reference (C++11 前)value_type& (C++11 起) |
const_reference | Allocator::const_reference (C++11 前) const value_type& (C++11 起) |
pointer | Allocator::pointer (C++11 前)std::allocator_traits<Allocator>::pointer (C++11 起) |
const_pointer Allocator::const_pointer (C++11 前) | std::allocator_traits<Allocator>::const_pointer (C++11 起) |
iterator | 双向迭代器(BidirectionalIterator) |
const_iterator | 常双向迭代器 |
reverse_iterator | std::reverse_iterator<iterator> |
const_reverse_iterator | std::reverse_iterator<const_iterator> |
2、成员函数
元素的访问
名称 | 说明 |
---|---|
at (C++11) | 访问指定的元素,同时进行越界检查 (公开成员函数) |
operator[] | 访问指定的元素 (公开成员函数) |
迭代器
名称 | 说明 |
---|---|
begin cbegin | 返回指向容器第一个元素的迭代器 (公开成员函数) |
end cend | 返回指向容器尾端的迭代器 (公开成员函数) |
rbegin crbegin | 返回一个指向容器最后一个元素的反向迭代器 (公开成员函数) |
rend crend | 返回一个指向容器前端的反向迭代器 (公开成员函数) |
容量
名称 | 说明 |
---|---|
empty | 检查容器是否为空 (公开成员函数) |
size | 返回容纳的元素数 (公开成员函数) |
max_size | 返回可容纳的最大元素数 (公开成员函数) |
修饰符
名称 | 说明 |
---|---|
clear | 删除全部内容 (公开成员函数) |
insert | 插入元素 (公开成员函数) |
emplace (C++11) | 就地构造元素 (公开成员函数) |
emplace_hint (C++11) | 使用hint 就地构造元素 (公开成员函数) |
erase | 删除元素 (公开成员函数) |
swap | 交换内容 (公开成员函数) 查找 |
count | 返回匹配特定键的元素数量 (公开成员函数) |
find | 寻找带有特定键的元素 (公开成员函数) |
equal_range | 返回匹配特定键的元素范围 (公开成员函数) |
lower_bound | 返回一个迭代器,指向第一个“不小于”给定值的元素 (公开成员函数) |
upper_bound | 返回一个迭代器,指向第一个“大于”给定值的元素 (公开成员函数) 观察器 |
key_comp | 返回用于比较键key的函数 (公开成员函数) |
value_comp | 返回用于在value_type 类型的对象中比较键的函数。 (公开成员函数) |
3、非成员函数
名称 | 说明 |
---|---|
std::swap(std::map) | 特化 std::swap 算法 (函数模板) |
operator== 、operator!= 、operator< 、operator<= 、operator> 、operator>= | 根据字典顺序比较 map 中的值 (函数模板) |
常用函数用法
构造函数
一般通过以下形式定义map:
map<T1,T2>name
T1为键值key的类型,T2为value键值类型
一个实例如下:
map<string , int >mapstring;
添加函数(insert)
我们首先要了解,一个map<T1,T2>
中存储的元素并不是一个个的key
和value
,而是一个个pair<T1,T2>
,所以我们进行添加元素的时候就是要添加一个个的pair
进去。
pair
(定义于头文件 <utility>
),同时这个头文件包含在<map>
中
pair
定义如下:
template<
class T1,
class T2
> struct pair;
其成员对象
成员 | 类型 |
---|---|
first | T1 |
second | T2 |
我们有多种方式可以对一个pair
进行初始化:
pair<string, int>person1("name", 16);
pair<string, int>person2 = { "name",15 };
pair<string, int>person3 = make_pair("name", 17);
map
和其他诸如vector
和list
的插入是不同的,它的插入不仅涉及到其内部的红黑树的遍历,还涉及到很多的比较以及红黑树枝干的调整,所以虽然插入之后的元素都已经根据键值大小被排好了序,但是每一次插入的开销也是很大的。
所以,我们用map
和其同类的关联容器set
、multmap
去取代排序算法用来排序元素的想法是很错误的。
insert
函数的系统定义如下(可看可不看):
std::pair<iterator,bool> insert( const value_type& value );
template <class P>
std::pair<iterator,bool> insert( P&& value );//(C++11 起)
iterator insert( iterator hint, const value_type& value );//(C++11 前)
iterator insert( const_iterator hint, const value_type& value );//(C++11 起)
template <class P>
iterator insert( const_iterator hint, P&& value );//(C++11 起)
template< class InputIt >
void insert( InputIt first, InputIt last );
void insert( std::initializer_list<value_type> ilist );
/*
参数
hint - 迭代器,用于插入内容,作为一个建议
value - 元素的值插入
first, last - 范围插入的元素
ilist - 初始化列表中插入的值
*/
我们就只说说最基本的一种。插入一个pair
。其他的就不多讲了,自己用一下就知道。
通过介绍pair
的多种初始化形式,map
也同样有很多种的方式(可能有更多的方式)进行pair
元素的添加:
map<string, int> persons;//定义
persons.insert(pair<string, int>("xiaohong", 3));//传入pair元素添加
persons["xiaoming"] = 6;//通过[]操作符添加(有相同键值的话就会改变这个键值的value)
persons.insert(map<string, int>::value_type("xiaogang", 7));//通过内置的typedef value_type 传入
persons.insert({ "xiaogang", 7 });//通过pair的多种定义方式自动转换成pair插入
persons.insert(make_pair("xiaobai", 17));//make_pair方式返回pair实体,再进行插入
查找函数(find)
被插入的元素都是根据其键值进行了排序,所以我们的查找顺序也大致如此。但是由于map
的实现是通过红黑树实现的,所以其也是一个二叉查找树
,对一个二叉查找树中的元素进行查找,效率是非常高的,查找的复杂度平均是Log(N),比如如果有1000个元素,最多查找10次,1,000,000个记录,最多查找20次。
下面是其查找函数的定义:
iterator find( const Key& key );
//key为要搜索的元素的键值
const_iterator find( const Key& key ) const;
//如果没有为key的元素,则返回end()的迭代器
以下为一个查找示例:
auto it = persons.find("xiaohong");
if (it!=persons.end())
{
cout << "find it the key is: " << it->first << " value is : " << it->second << endl;
}
删除函数(erase)
删除函数,其实不用过多的介绍其内部构造了,我们需要知道的就是,在关联容器中,插入和删除一个元素的开销都是很高的,至于为什么就不多提,主要讲这个函数的原理和使用方法.
以下为map::erase()
函数的定义:
void erase( iterator position );//(C++11 前) //(1)
iterator erase( const_iterator position );//(C++11 起)
void erase( iterator first, iterator last );//(C++11 前) (2)
iterator erase( const_iterator first, const_iterator last );//(C++11 起)
size_type erase( const key_type& key );// (3)
/*
1) 删除的元素pos.
2) 在范围[first; last)内移出元素pos.
3) 在所有元素中移出键值为key的元素
*/
-------------------------
/*
参数
pos - 要移除的元素的迭代器
first, last - 要移除的元素范围
key - 要移除的元素的键值
*/
在每次删除和添加元素的时候,map
内部的红黑树都会进行一定的重新构建。不过不管是erase()
还是insert()
的时候,受影响的只是这一个节点,其他节点的内存位置并不会发生改变,因为其实现是节点型的红黑树。
以下为简单erase()
的示例:
for (auto it = persons.begin(); it != persons.end();)
{
if (it->first == "xiaohong")it = persons.erase(it);
else
{
cout << it->first << " " << it->second << endl;
it++;
}
}
注意,erase(it)
之后,it
指向的节点就已经被删除了,所以这个迭代器就不能进行使用了,erase()
函数会返回一个指向it
后一个节点的迭代器,我们让it = persons.erase(it)
就可以保证没有错误了。