STL-map和multimap

根据应用场景的不同,STL共实现了两种不同结构的管理式容器:树型结构与哈希结构。树型结构的关联式容器主要有四种:map、set、multimap、multiset。这四种容器的共同点是:使用平衡二叉树(红黑树)作为其底层结构,容器中的元素是一个有序的序列。

map的特点

  1. map的所有元素都会根据元素的键值自动被排序。
  2. 在map中,键值key通常用于排序和唯一地标识元素,而值value中存储与此键值key关联的内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型value_type绑定在一起,为其取别名为pair:typedef pair value_type;
  3. 在内部,map中的元素总是按照键值key进行比较排序的,默认从小到大
  4. map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序 对元素进行直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)
  5. map支持下标访问符,即在[]中放入key,就可以找到与key对应的value的引用。其实在map中,中括号已经被重载为有不存在即插入的功能(对于自定义类型会调用值的默认构造函数,对于int类型则为0)。于是如果查找的key不存在,则map中会增加很多垃圾数据
  6. map通常被实现为二叉搜索树(更准确的说:平衡二叉搜索树 (红黑树)),查找效率比较高为:log_2 N
  7. 我们通过map的迭代器不可以改变元素的键值,因为map元素的键值关系到map元素的排列规则,任意改变map元素键值将会严重破坏map组织。但如果想要修正元素的实值是可以的
  8. 当客户端对他进行插入或者删除操作时,操作之前的所有迭代器在操作之后都是有效的(删除情况下除了被删除的那个元素)
template <class T1, class T2>
struct pair {
  typedef T1 first_type;
  typedef T2 second_type;

  T1 first;
  T2 second;
  pair() : first(T1()), second(T2()) {}
  pair(const T1& a, const T2& b) : first(a), second(b) {}

#ifdef __STL_MEMBER_TEMPLATES
  template <class U1, class U2>
  pair(const pair<U1, U2>& p) : first(p.first), second(p.second) {}
#endif
};

class map {
public:

// typedefs:

  typedef Key key_type;
  typedef T data_type;
  typedef T mapped_type;
  typedef pair<const Key, T> value_type;
  typedef Compare key_compare;
    
  class value_compare
    : public binary_function<value_type, value_type, bool> {
  friend class map<Key, T, Compare, Alloc>;
  protected :
    Compare comp;
    value_compare(Compare c) : comp(c) {}
  public:
    bool operator()(const value_type& x, const value_type& y) const {
      return comp(x.first, y.first);
    }
  };

private:
  typedef rb_tree<key_type, value_type, 
                  select1st<value_type>, key_compare, Alloc> rep_type;
  rep_type t;  // red-black tree representing map
  
  // 注意这个重载函数
  T& operator[](const key_type& k) {
    return (*((insert(value_type(k, T()))).first)).second;
  }

};

map原型

std::map
template <class Key,
				 class T,
				 class Compare = less<Key>,
				 class Alloc = allocator<pair<const Key, T> >
				 > class map;
key:键值对中的key的类型
T:键值中value的类型
Compare:比较器的类型,map中的元素是按照key来比较的,缺省情况下按照小于来比较,一般情况下(内置类型元素)
该参数不需要传递,如果无法比较时(自定义类型),需要用户自己显式传递比较规则(一般情况下按照函数指针或者仿函数来传递)
Alloc:通过空间配置器来申请底层空间,不需要用户传递,除非用户不想使用标注库提供的空间配置器

构造函数

函数名称 函数声明
map(const key_compare& comp = key_compare(), allocator_type &alloc = allocator_type()) const 构造一个空的map
template map(InputIterator first, InputIterator last, const key_compare& comp = key_compare(), const alloctor_type &alloc = alloctor_type()) 用first到last区间中的元素构造map
map(const map&x) map的拷贝构造
void test01()
{
    // key和value的类型给成字符串
    map<string,string> m1;
    // C++11 的列表初始化
    map<string,string> m2{{"apple", "苹果"},
                          {"banana", "香蕉"},
                          {"orange", "橘子"},
                          {"peach", "桃子"}};
    //cout << m2["apple"] << endl;
  
    for(auto it = m2.begin(); it != m2.end(); ++it)
    {
        cout << (*it).first << "--->" << it->second << endl;
    }
    map<string,string> m3(m2);
}

容量

函数名称 函数声明
bool empty()const 检测map中的元素是否为空,如果为空返回true,否则返回false
size_type size()const 返回map中有效元素的个数

修改与访问操作

函数名称 函数声明
pair insert(const value_type &x) 在map中插入键值对x,iterator代表插入元素的位置,bool代表是否插入成功
iterator insert(iterator position, const value_type &x 在position位置插入值为x的键值对,返回该键值对在map中的位置,注意:元素不一定必须插入在position位置,该位置指示一个参考
template void insert(InputIterator first, InputIterator last) 在map中插入first到last区间中的元素
void erase(iterator position) 删除position位置上的元素
void erase(iterator first,iteraator last) 删除first到last区间中的元素
void swap(map &mp) 交换两个map中的元素
void clear() 将map中的元素清空
iterator find(const key_type& x) 在map中查找key为x的元素,找到返回该元素的位置迭代器,否则返回end
const_iterator find(const key_type& x) 在map中查找key为x的元素,找到返回该元素的位置const迭代器,否则返回cend
size_type count(const key_type &x)const 返回Key为x的键值在map中的个数,注意map 中的key是唯一的,因此该函数的返回值要么为0,要么为1,因此可以用该函数来检测一个key是否在map中
mapped_type& at (const key_type& k) 返回键值k对应value的引用,如果k不在map中,那么out_of_range exception异常抛出(C++11新特性)
mapped_type& operator[](const key_type& k) 返回key对应的value,可以通过返回查看当前元素是否在map中
void test03()
{
    map<string,string> m;
    // 向map中插入元素的方式
    // 将键值对<"peach", "桃子">插入到Map,用pair直接来构造键值对
    m.insert(pair<string, string>("peach", "桃子"));

    //将键值对<"watermel", "西瓜">插入到map中,用make_pair函数来构造键值对
    m.insert(make_pair("watermel", "西瓜"));
    
    // 先拿到该容器中的键值对类型,然后插入
    m.insert(map<string,string>::value_type("peach", "桃子"));
  
    // key不存在时抛异常
    //m.at("banan") = "香蕉";
    
    // 这个的原理是,先用key和T()构造一个键值对,然后调用insert函数将该键值对插入到map中
    // 如果key已经存在,插入失败,insert函数返回该key所在位置的迭代器,
    // 如果key不存在,插入成功,insert函数返回新插入元素所在位置的迭代器,
    // operator[]函数最后将insert的返回值中的value返回
    m["banan"] = "香蕉";
   
   // 输出Map的大小
    cout << m.size() << endl;

    // 用迭代器去遍历map中的元素,可以得到一个按照key排序的序列
    for(auto &e : m)
    {
        cout << e.first << "--->" << e.second << endl;
    }
    cout << endl;
    
    // map中的键值对key一定是唯一的,如果key存在将插入失败
    auto ret = m.insert(make_pair("peach", "桃色"));
    if(ret.second)
    {
        cout << "不在map中,已经插入" << endl;
    }
    else
    {
        cout << "键值为peach已经在map:" << ret.first->first << ":" << ret.first->second << endl;
    }

    // 删除key为apple的元素
    m.erase("apple");
    for(auto&e : m)
    {
        cout << e.first << "--->" << e.second << endl;
    }

    if(1 == m.count("apple"))
    {
        cout << "apple还在" << endl;
    }
    else
    {
        cout << "apple被吃了" << endl;
    }
} 

自定义排序

class myCompare
{
public:
    bool operator()(int v1, int v2)
    {
        return v1 > v2;
    }
};

void test3_1()
{
    map<int, int, myCompare> m;
    m.insert(pair<int, int>(1,10));
    m.insert(make_pair(2,20));
    m.insert(map<int,int>::value_type(3,30));
    m[4] = 40;
    for(map<int, int, myCompare>::iterator it = m.begin(); it != m.end(); it++)
    {
        cout << "key :" << it->first << "value :" << it->second << endl;
    }
}

map插入数据的三种方法

  1. 通过 insert 函数插入 pair
  2. 通过insert 函数插入 value_type(其实value_type就是pair的typedef)
  3. 用数组方式插入数据

区别:以上三种用法,虽然都可以实现数据的插入,但是它们是有区别的。第一种和第二种在效果上是完成一样的,用 insert 函数插入数据,在数据的插入上涉及到集合的唯一性这个概念,即当 map 中已经存在某个关键字时,insert 操作是不能成功插入数据,但是用数组方式则可以,它可以覆盖以前该关键字对应的值。

multimap

  1. multimap是关联容器,它按照特定的次序(按照key来比较)存储由键值Key和值value组合而成的键值对,其中多个键值对之间是可以重复的。
  2. 在multimap中,键值key通常用于排序和唯一地标识元素,而值value中存储与此键值key关联的内容。键值key和值value的类型可能不同,并且在multimap的内部,key与value通过成员类型value_type绑定在一起,value_type是组合key和value的键值对:typedef pair value_type;
  3. 在内部,multimap中的元素总是按照键值key进行比较排序的,默认从小到大
  4. multimap中通过键值访问单个元素的速度通常比unordered_multimap容器慢,但multimap允许根据顺序对元素进行直接迭代,从而可以得到一个有序的序列
  5. multimap通常被实现为二叉搜索树(更准确的说:平衡二叉搜索树 (红黑树)),查找效率比较高为:log_2 N
  6. multimap和map的唯一区别就是:map中的key是唯一的,而multimap中key是可以重复的。
  7. multimap中没有重载operator[]操作。
void test04()
{
    multimap<string, string> m;
    m.insert(make_pair("李逵", "黑旋风"));
    m.insert(make_pair("林冲", "豹子头"));
    m.insert(make_pair("鲁达", "花和尚"));
    m.insert(make_pair("李逵", "铁牛"));
    
    cout << m.size() << endl;
    
    for(auto& e : m)
    {
        cout << "<" << e.first << "," << e.second << ">" << endl;
    }
    cout << m.count("李逵") << endl;
    
}

void test05()
{
    multimap<int, int> m;
    for(int i = 0; i < 10; ++i)
    {
        m.insert(pair<int,int>(i, i));
    }
    for(auto& e : m)
    {
        cout << e.first << "--->" << e.second << endl;
    }
    cout << endl;

    auto it = m.lower_bound(5);
    cout << it->first << "--->" << it->second << endl;

    //返回m中大于5的元素
    it = m.upper_bound(5);
    cout << it->first << "--->" << it->second << endl;
}

你可能感兴趣的:(STL)