STL:map详解

map详解

map的介绍

  1. map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元素。
  2. 在map中,键值key通常用于排序和惟一地标识元素,而值value中存储与此键值key关联的内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型value_type绑定在一起,为其取别名称为 pair: typedef pair<const key, T> value_type
  3. 在内部,map中的元素总是按照键值key进行比较排序的。
  4. map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序对元素进行直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)。
  5. map支持下标访问符,即在[]中放入key,就可以找到与key对应的value。
  6. 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的迭代器

  1. 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
  1. cbegin()和cend()
    与begin()和end()意义相同,但cbegin()和cend()所指向的元素不能修改
const_reverse_iterator crbegin() const noexcept;
const_reverse_iterator crend() const noexcept;
  1. 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
  1. 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
  1. 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[]中实际进行插入查找。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ryan.Alaskan Malamute

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值