文章目录
map详解
map的介绍
- map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元素。
- 在map中,键值key通常用于排序和惟一地标识元素,而值value中存储与此键值key关联的内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型value_type绑定在一起,为其取别名称为 pair:
typedef pair<const key, T> value_type
- 在内部,map中的元素总是按照键值key进行比较排序的。
- map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序对元素进行直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)。
- map支持下标访问符,即在[]中放入key,就可以找到与key对应的value。
- map通常被实现为二叉搜索树(更准确的说:平衡二叉搜索树(红黑树))。
map的使用
使用map时需要加入头文件#include<map>
map的模板参数
template < class Key, // map::key_type
class T, // map::mapped_type
class Compare = less<Key>, // map::key_compare
class Alloc = allocator<pair<const Key,T> > // map::allocator_type
> class map;
- Key:键值对
- T :键值对对应的Value值
- Compare :比较器的类型,map中的元素是按照key来比较的,缺省情况下按照小于来比较,即
less<Key>
,降序排序。
一般情况下(内置类型元素)该参数不需要传递,如果无法比较时(自定义类型),需要用户自己显式传递比较规则(一般情况下按照函数指针或者仿函数来传递) - Alloc:通过空间配置器来申请底层空间,不需要用户传递,除非用户不想使用标准库提供的
空间配置器
map的构造函数
struct classcomp {
bool operator() (const char& lhs, const char& rhs) const
{ return lhs<rhs; }
};
int main ()
{
std::map<char,int> first;
//添加元素
first['a']=10;
first['b']=30;
first['c']=50;
std::map<char,int> second (first.begin(),first.end()); //迭代器构造
std::map<char,int> third (second); //复制构造
std::map<char,int,classcomp> fourth; //构造时指定比较器
-
map可以直接用空容器构造函数(默认构造函数)构造,即指定键值类型和对应的值value类型即可
std::map<char,int> first;
, 构造一个没有元素的空容器。 -
用map的迭代器进行构造,
std::map<char,int> second (first.begin(),first.end());
-
复制构造函数,构造一个map的副本
std::map<char,int> third (second);
-
构造时指定比较器(通过仿函数实现)
std::map<char,int,classcomp> fourth
map的迭代器
- begin() 和 end()
std::map<char,int> mymap;
mymap['b'] = 100;
mymap['a'] = 200;
mymap['c'] = 300;
// show content:
for (std::map<char,int>::iterator it=mymap.begin(); it!=mymap.end(); ++it)
std::cout << it->first << " => " << it->second << '\n';
- begin():首元素的位置
- end() :最后一个元素的下一个位置
rbegin()和rend()是map的迭代器,可以从头到尾依次遍历元素。
运行结果:
a => 200
b => 100
c => 300
- cbegin()和cend()
与begin()和end()意义相同,但cbegin()和cend()所指向的元素不能修改
const_reverse_iterator crbegin() const noexcept;
const_reverse_iterator crend() const noexcept;
- rbegin()和rend()
rbegin()和rend()是map的反向迭代器,rbegin在end位置,rend在begin位置,其++和–操作与begin和end操作移动相反,即rbegin()的++操作是从尾部开始向头部遍历,rend()的–操作是从头部开始向尾部遍历
示例:
std::map<char,int> mymap;
mymap['x'] = 100;
mymap['y'] = 200;
mymap['z'] = 300;
// show content:
std::map<char,int>::reverse_iterator rit;
for (rit=mymap.rbegin(); rit!=mymap.rend(); ++rit)
std::cout << rit->first << " => " << rit->second << '\n';
运行结果:
z => 300
y => 200
x => 100
- crbegin()和crend()
与rbegin()和rend()意义相同,但crbegin()和crend()所指向的元素不能修改
const_reverse_iterator crbegin() const noexcept;
const_reverse_iterator crend() const noexcept;
map的容量与元素访问
empty
检测map中的元素是否为空,是返回true,否则返回false
示例:
std::map<char,int> mymap;
mymap['a']=10;
mymap['b']=20;
mymap['c']=30;
while (!mymap.empty())
{
std::cout << mymap.begin()->first << " => " << mymap.begin()->second << '\n';
mymap.erase(mymap.begin());
}
运行结果:
a => 10
b => 20
c => 30
size
返回map中有效元素的个数
std::map<char,int> mymap;
mymap['a']=101;
mymap['b']=202;
mymap['c']=302;
std::cout << "mymap.size() is " << mymap.size() << '\n';
运行结果:
mymap.size() is 3
insert
map的插入函数,将键值对插入到map中,有三种形式的 insert ,这里逐一介绍。
- 直接插入元素
在map中插入键值对val,(注意:val是一个键值对),返回值也是键值对:iterator代表新插入元素的位置,bool代表释放插入成功
函数原型:
pair<iterator,bool> insert (const value_type& val);
使用示例:
std::map<char,int> mymap;
mymap.insert ( std::pair<char,int>('a',100) );
mymap.insert ( std::pair<char,int>('z',200) );
需要注意的是在插入时放入的元素是键值对std::pair<char,int>('a',100)
- 插入元素时指定迭代器
在map插入键值对 val 时同时给出迭代器,迭代器给的合适能提高插入的效率
函数原型:
iterator insert (iterator position, const value_type& val);
使用示例:
std::map<char,int>::iterator it = mymap.begin();
mymap.insert (it, std::pair<char,int>('b',300)); // 高效
mymap.insert (it, std::pair<char,int>('c',400)); // 不是最高效的
- 迭代器区间插入
在map插入时不使用键值对的方式,使用另一个map的迭代器进行插入
函数原型:
template <class InputIterator>
void insert (InputIterator first, InputIterator last);
使用示例:
std::map<char,int> mymap;
mymap.insert ( std::pair<char,int>('a',100) );
mymap.insert ( std::pair<char,int>('z',200) );
mymap.insert (it, std::pair<char,int>('b',300));
mymap.insert (it, std::pair<char,int>('c',400));
std::map<char,int> anothermap;
anothermap.insert(mymap.begin(),mymap.find('c'));
- insert 函数的返回值
通过下面的函数原型我们可以知道,insert函数在插入后,不管成功与否都会返回一个pair类型的键值对pair<iterator,bool>
pair<iterator,bool> insert (const value_type& val);
- 如果插入成功,那么pair.second为true,pair.second返回的是当前插入位置的迭代器
- 如果插入失败。那么pair.second为false;
std::pair< std::map<char,int>::iterator , bool > ret;
ret = mymap.insert ( std::pair<char,int>('z',500) );
if (ret.second==false) {
std::cout << "element 'z' already existed";
std::cout << " with a value of " << ret.first->second << '\n';
}
运行结果:
element 'z' already existed with a value of 200
mymap contains:
emplace
构造和插入元素,该函数同insert一样具有插入元素的功能。但与insert不同的是, insert是复制现有的对象插入到map容器中,而emplace只需要给出相应的键值key和对应的value值。那么该函数将会自动调用构造函数并放入map容器中。
也就是说insert需要先构造一个pair类型的键值对,然后传递给insert函数。而emplace可以直接给出具体的值,该函数会自动调用构造函数生成pair类型的键值对并插入到map容器。
函数原型:
template <class... Args>
pair<iterator,bool> emplace (Args&&... args);
使用示例:
std::map<char,int> mymap;
mymap.emplace('x',100);
mymap.emplace('y',200);
mymap.emplace('z',100);
std::cout << "mymap contains:";
for (auto& x: mymap)
std::cout << " [" << x.first << ':' << x.second << ']';
std::cout << '\n';
运行结果:
mymap contains: [x:100] [y:200] [z:100]
insert 和 emplace 的区别:
同样插入'a' , 100
键值对
mymap.emplace('a',100);
mymap.insert ( std::pair<char,int>('a',100) );
emplace_hint
构造并插入带有提示的元素,函数给出一个具有提示作用的迭代器,该元素将按照其内部比较对象描述的顺序插入到其相应位置,但函数使用提示的迭代器开始搜索插入点,从而在实际插入点位于位置或靠近它时大大加快了该过程。
函数原型:
template <class... Args>
iterator emplace_hint (const_iterator position, Args&&... args);
使用示例:
std::map<char,int> mymap;
auto it = mymap.end();
it = mymap.emplace_hint(it,'b',10);
mymap.emplace_hint(it,'a',12);
mymap.emplace_hint(mymap.end(),'c',14); //提示在mymap的最后,这将加快插入的过程
erase
map的删除函数,将目标键值从map中删除,map的删除函数有三种形式:
- 指定迭代器删除
给定迭代器的位置,进行删除。
(注意:如果给定的迭代器是无效的,那么会导致出现未定义的行为,不推荐这么做)
函数原型:
void erase (iterator position);
使用示例:
std::map<char, int> mymap;
std::map<char, int>::iterator it;
mymap['a'] = 10;
mymap['b'] = 20;
mymap['c'] = 30;
mymap['d'] = 40;
mymap['e'] = 50;
mymap['f'] = 60;
it = mymap.find('b');
mymap.erase(it);
使用迭代器将键值为’ b ’ 的元素删除。
- 指定键值删除
给定键值进行删除
函数原型:
size_type erase (const key_type& k);
使用示例:
mymap.erase('c');
直接给出需要删除的键值,将 ’ c ’ 删除。
返回值: 该函数返回擦除的元素数,返回的是成员类型 size_type ,无符号整数类型。
- 迭代器区间删除
函数原型:
void erase (iterator first, iterator last);
使用示例:
it = mymap.find('e');
mymap.erase(it, mymap.end()); // erasing by range
给出了 ’ e ’ 位置的迭代器,将map中从 ’ e ’ 位置到map的结尾处的所有键值都删除了。
clear
从map容器中删除所有元素(这些元素将被销毁),使容器的大小为 0。
函数原型:
void clear();
使用示例:
std::map<char,int> mymap;
mymap['x']=100;
mymap['y']=200;
mymap['z']=300;
cout<<mymap.size()<<endl; // 输出结果 3
mymap.clear();
cout<<mymap.size()<<endl; // 输出结果 0
运行结果:
3
0
- find
在容器中搜索键等效于 k 的元素,如果找到,则返回一个迭代器,否则返回一个迭代器给为map.end()
如果 map.find()是const类型,则该函数返回const_iterator。
函数原型:
iterator find (const key_type& k);
const_iterator find (const key_type& k) const;
std::map<char,int> mymap;
std::map<char,int>::iterator it; //声明一个迭代器
mymap['a']=50;
mymap['b']=100;
it = mymap.find('b');
if (it != mymap.end()) //查找成功
mymap.erase (it);
查找成功则返回一个指向目标键值的迭代器,否则指向map::end;
注意:返回值是成员类型 iterator 和 const_iterator ,是指向元素(类型为 pair<const key_type,mapped_type>)的双向迭代器类型。
count
在容器中搜索键等效于 k 的元素,并返回匹配项数,由于map容器中的所有元素都是唯一的,因此该函数只能返回 1(如果找到该元素)或零(否则)。
函数原型:
size_type count (const key_type& k) const;
使用示例:
std::map<char,int> mymap;
char c;
mymap ['a']=101;
mymap ['c']=202;
mymap ['f']=303;
for (c='a'; c<'h'; c++)
{
std::cout << c;
if (mymap.count(c)>0)
std::cout << " is an element of mymap.\n";
else
std::cout << " is not an element of mymap.\n";
}
运行结果:
a is an element of mymap.
b is not an element of mymap.
c is an element of mymap.
d is not an element of mymap.
e is not an element of mymap.
f is an element of mymap.
g is not an element of mymap.
返回值:返回值是成员类型 size_type, 无符号整数类型。
max_size
返回map容器可以容纳的最大元素数。
函数原型:
size_type max_size() const;
使用示例:
int i;
std::map<int,int> mymap;
if (mymap.max_size()>1000)
{
for (i=0; i<1000; i++) mymap[i]=0;
std::cout << "The map contains 1000 elements.\n";
}
else std::cout << "The map could not hold 1000 elements.\n";
如果map可以插入的最大元素数大于1000,那么插入1000个元素。
返回值:返回值是成员类型 size_type, 无符号整数类型。
lower_bound
返回一个迭代器,指向容器中第一个大于等于k的元素。
函数原型:
iterator lower_bound (const key_type& k);
const_iterator lower_bound (const key_type& k) const;
使用示例:
std::map<char,int> mymap;
std::map<char,int>::iterator itlow,itup;
mymap['a']=20;
mymap['b']=40;
mymap['c']=60;
mymap['e']=100;
itlow=mymap.lower_bound ('b'); // itlow points to b
itlow_1=mymap.lower_bound ('d'); // itlow points to e
lower_bound ('b')
,返回大于等于键值b的迭代器,这里b存在,那么返回指向b处的迭代器;
lower_bound ('d')
,返回大于等于键值d的迭代器,这里d不存在,那么返回第一个大于d的,即e处的迭代器;
upper_bound
返回一个迭代器,指向容器中的第一个大于键值k的元素。
函数原型:
iterator upper_bound (const key_type& k);
const_iterator upper_bound (const key_type& k) const;
使用示例:
std::map<char,int> mymap;
std::map<char,int>::iterator itlow,itup;
mymap['a']=20;
mymap['b']=40;
mymap['d']=80;
mymap['e']=100;
itup=mymap.upper_bound ('c'); // itup points to d
itup=mymap.upper_bound ('d'); // itup points to e (not d!)
upper_bound ('c')
,返回大于键值c的迭代器,这里第一个大于c的是d,那么返回指向d处的迭代器;
upper_bound ('d')
,返回大于键值d的迭代器,这里第一个大于d的是e,那么返回指向e处的迭代器;
swap
交换两个map容器,将两个map容器中的内容对换
函数原型:
void swap (map& x);
使用示例:
std::map<char,int> foo,bar;
foo['x']=100;
foo['y']=200;
bar['a']=11;
bar['b']=22;
bar['c']=33;
foo.swap(bar);
std::cout << "foo contains:\n";
for (std::map<char,int>::iterator it=foo.begin(); it!=foo.end(); ++it)
std::cout << it->first << " => " << it->second << '\n';
std::cout << "bar contains:\n";
for (std::map<char,int>::iterator it=bar.begin(); it!=bar.end(); ++it)
std::cout << it->first << " => " << it->second << '\n';
使用示例:
foo contains:
a => 11
b => 22
c => 33
bar contains:
x => 100
y => 200
at
返回对用键 k 标识的元素的映射值的引用。
如果 k 与容器中任何元素的键不匹配,则该函数将引发out_of_range异常。也就是说at函数访问的键值必须是map容器中存在的,否则就会引发异常。
函数原型:
mapped_type& at (const key_type& k);
const mapped_type& at (const key_type& k) const;
使用示例:
std::map<std::string,int> mymap = {
{ "alpha", 0 },
{ "beta", 0 },
{ "gamma", 0 } };
mymap.at("alpha") = 10;
mymap.at("beta") = 20;
mymap.at("gamma") = 30;
for (auto& x: mymap) {
std::cout << x.first << ": " << x.second << '\n';
}
operator[]
- 如果 k 与容器中元素的键匹配,则该函数将返回对其映射值的引用。
- 如果 k 与容器中任何元素的键不匹配,则该函数将插入具有该键的新元素,并返回对其映射值的引用。请注意,这始终会将容器大小增加 1,即使没有为元素分配映射值(元素是使用其默认构造函数构造的)
这将使map容器具有类似访问数组的特性
函数原型:
mapped_type& operator[] (const key_type& k);
使用示例:
std::map<char,std::string> mymap;
mymap['a']="an element";
mymap['b']="another element";
mymap['c']=mymap['b'];
std::cout << "mymap['a'] is " << mymap['a'] << '\n';
std::cout << "mymap['b'] is " << mymap['b'] << '\n';
std::cout << "mymap['c'] is " << mymap['c'] << '\n';
std::cout << "mymap['d'] is " << mymap['d'] << '\n';
std::cout << "mymap now contains " << mymap.size() << " elements.\n";
return 0;
运行结果:
mymap['a'] is an element
mymap['b'] is another element
mymap['c'] is another element
mymap['d'] is
mymap now contains 4 elements.
当元素存在时,mymap['d']
会返回键值 d 对应的value的引用,也就使说,可以通过重载的 [] 运算符获取到 key 为 d 时的元素,也能通过其直接修改 key 为 d 时相应的 value 值。
总结:
- map中的的元素是键值对
- map中的key是唯一的,并且不能修改
- 默认按照小于的方式对key进行比较
- map中的元素如果用迭代器去遍历,可以得到一个有序的序列
- map的底层为平衡搜索树(红黑树),查找效率比较高 O ( l o g 2 N ) O(log_2 N) O(log2N)
- 支持[]操作符,operator[]中实际进行插入查找。