STL源码剖析 [容器](十七)[stl_multimap.h]

multimap 与 map 一样,都是使用红黑树对记录型的元素数据,按元素键值的比较关系,进行快速的插入、删除和检索操作,所不同的是 multimap 允许将具有重复键值的元素插入容器。在 multimap 容器中,元素的键值与元素的映照数据的映照关系,是多对多的,因此,multimap 称为多重映照容器。multimap 与 map 之间的多重特性差异,类似于 multiset 与 set 的多重特性差异。
    multimap 多重映照容器实现了 Sorted Associative Container 、Pair Associative Container 和 Multimap Associative Container 概念的接口规范。

multimap源码:

// Filename:    stl_multimap.h

// Comment By:  凝霜
// E-mail:      [email protected]
// Blog:        http://blog.csdn.net/mdl13412

/*
 *
 * Copyright (c) 1994
 * Hewlett-Packard Company
 *
 * Permission to use, copy, modify, distribute and sell this software
 * and its documentation for any purpose is hereby granted without fee,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear
 * in supporting documentation.  Hewlett-Packard Company makes no
 * representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied warranty.
 *
 *
 * Copyright (c) 1996,1997
 * Silicon Graphics Computer Systems, Inc.
 *
 * Permission to use, copy, modify, distribute and sell this software
 * and its documentation for any purpose is hereby granted without fee,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear
 * in supporting documentation.  Silicon Graphics makes no
 * representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied warranty.
 */

/* NOTE: This is an internal header file, included by other STL headers.
 *   You should not attempt to use it directly.
 */

#ifndef __SGI_STL_INTERNAL_MULTIMAP_H
#define __SGI_STL_INTERNAL_MULTIMAP_H

__STL_BEGIN_NAMESPACE

#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32)
#pragma set woff 1174
#endif

// 如果编译器不能根据前面模板参数推导出后面使用的默认参数类型,
// 那么就需要手工指定, 本实作multimap内部元素默认使用less进行比较
// 内部维护的数据结构是红黑树, 具有非常优秀的最坏情况的时间复杂度
// 注意: 与map不同, multimap允许有重复的元素
#ifndef __STL_LIMITED_DEFAULT_TEMPLATES
template <class Key, class T, class Compare = less<Key>, class Alloc = alloc>
#else
template <class Key, class T, class Compare, class Alloc = alloc>
#endif
class multimap
{
public:
  // 这里和map定义相同, 见<setL_map.h>
  typedef Key key_type;
  typedef T data_type;
  typedef T mapped_type;
  typedef pair<const Key, T> value_type;
  typedef Compare key_compare;

  // 关于为什么继承自binary_function见<stl_function.h>中的讲解
  // 被嵌套类提供key的比较操作
  class value_compare : public binary_function<value_type, value_type, bool>
  {
  friend class multimap<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:
  // 内部采用红黑树为数据结构, 其实现在<stl_tree.h>
  typedef rb_tree<key_type, value_type,
                  select1st<value_type>, key_compare, Alloc> rep_type;
  rep_type t;  // red-black tree representing multimap

public:
  // 标记为'STL标准强制要求'的typedefs用于提供iterator_traits<I>支持
  // 注意: 迭代器, 引用类型都设计为const, 这是由multimap的性质决定的,
  //      如果用户自行更改其数值, 可能会导致内部的红黑树出现问题
  typedef typename rep_type::pointer pointer;                  // STL标准强制要求
  typedef typename rep_type::const_pointer const_pointer;
  typedef typename rep_type::reference reference;              // STL标准强制要求
  typedef typename rep_type::const_reference const_reference;
  typedef typename rep_type::iterator iterator;                // STL标准强制要求
  typedef typename rep_type::const_iterator const_iterator;
  typedef typename rep_type::reverse_iterator reverse_iterator;
  typedef typename rep_type::const_reverse_iterator const_reverse_iterator;
  typedef typename rep_type::size_type size_type;
  typedef typename rep_type::difference_type difference_type;  // STL标准强制要求

  multimap() : t(Compare()) { }
  explicit multimap(const Compare& comp) : t(comp) { }

#ifdef __STL_MEMBER_TEMPLATES
  template <class InputIterator>
  multimap(InputIterator first, InputIterator last)
    : t(Compare()) { t.insert_equal(first, last); }

  template <class InputIterator>
  multimap(InputIterator first, InputIterator last, const Compare& comp)
    : t(comp) { t.insert_equal(first, last); }
#else
  multimap(const value_type* first, const value_type* last)
    : t(Compare()) { t.insert_equal(first, last); }
  multimap(const value_type* first, const value_type* last,
           const Compare& comp)
    : t(comp) { t.insert_equal(first, last); }

  multimap(const_iterator first, const_iterator last)
    : t(Compare()) { t.insert_equal(first, last); }
  multimap(const_iterator first, const_iterator last, const Compare& comp)
    : t(comp) { t.insert_equal(first, last); }
#endif /* __STL_MEMBER_TEMPLATES */

  multimap(const multimap<Key, T, Compare, Alloc>& x) : t(x.t) { }

  multimap<Key, T, Compare, Alloc>&
  operator=(const multimap<Key, T, Compare, Alloc>& x)
  {
    t = x.t;
    return *this;
  }

  // 返回用于key比较的函数
  key_compare key_comp() const { return t.key_comp(); }

  // 由于multimap的性质, value比较和key使用同一个比较函数
  value_compare value_comp() const { return value_compare(t.key_comp()); }

  iterator begin() { return t.begin(); }
  const_iterator begin() const { return t.begin(); }
  iterator end() { return t.end(); }
  const_iterator end() const { return t.end(); }
  reverse_iterator rbegin() { return t.rbegin(); }
  const_reverse_iterator rbegin() const { return t.rbegin(); }
  reverse_iterator rend() { return t.rend(); }
  const_reverse_iterator rend() const { return t.rend(); }
  bool empty() const { return t.empty(); }
  size_type size() const { return t.size(); }
  size_type max_size() const { return t.max_size(); }

  // 这里调用的是专用的swap, 不是全局的swap, 定于于<stl_tree.h>
  void swap(multimap<Key, T, Compare, Alloc>& x) { t.swap(x.t); }

  // 插入元素, 注意, 插入的元素key允许重复
  iterator insert(const value_type& x) { return t.insert_equal(x); }

  // 在position处插入元素, 但是position仅仅是个提示, 如果给出的位置不能进行插入,
  // STL会进行查找, 这会导致很差的效率
  iterator insert(iterator position, const value_type& x)
  {
    return t.insert_equal(position, x);
  }

#ifdef __STL_MEMBER_TEMPLATES
  template <class InputIterator>
  void insert(InputIterator first, InputIterator last)
  {
    t.insert_equal(first, last);
  }
#else
  void insert(const value_type* first, const value_type* last) {
    t.insert_equal(first, last);
  }
  void insert(const_iterator first, const_iterator last) {
    t.insert_equal(first, last);
  }
#endif /* __STL_MEMBER_TEMPLATES */

  // 擦除指定位置的元素, 会导致内部的红黑树重新排列
  void erase(iterator position) { t.erase(position); }

  // 会返回擦除元素的个数
  size_type erase(const key_type& x) { return t.erase(x); }

  // 擦除指定区间的元素, 会导致红黑树有较大变化
  void erase(iterator first, iterator last) { t.erase(first, last); }

  // 好吧, clear all, 再见吧红黑树
  void clear() { t.clear(); }

  // 查找指定的元素
  iterator find(const key_type& x) { return t.find(x); }
  const_iterator find(const key_type& x) const { return t.find(x); }

  // 返回指定元素的个数
  size_type count(const key_type& x) const { return t.count(x); }

  // 返回小于当前元素的第一个可插入的位置
  iterator lower_bound(const key_type& x) {return t.lower_bound(x); }
  const_iterator lower_bound(const key_type& x) const
  {
    return t.lower_bound(x);
  }

  // 返回大于当前元素的第一个可插入的位置
  iterator upper_bound(const key_type& x) {return t.upper_bound(x); }
  const_iterator upper_bound(const key_type& x) const
  {
    return t.upper_bound(x);
  }

  pair<iterator,iterator> equal_range(const key_type& x)
  {
    return t.equal_range(x);
  }

  pair<const_iterator,const_iterator> equal_range(const key_type& x) const
  {
    return t.equal_range(x);
  }

  friend bool operator== __STL_NULL_TMPL_ARGS (const multimap&,
                                               const multimap&);
  friend bool operator< __STL_NULL_TMPL_ARGS (const multimap&,
                                              const multimap&);
};

// 比较两个multimap比较的是其内部的红黑树, 会触发红黑树的operator

template <class Key, class T, class Compare, class Alloc>
inline bool operator==(const multimap<Key, T, Compare, Alloc>& x,
                       const multimap<Key, T, Compare, Alloc>& y)
{
  return x.t == y.t;
}

template <class Key, class T, class Compare, class Alloc>
inline bool operator<(const multimap<Key, T, Compare, Alloc>& x,
                      const multimap<Key, T, Compare, Alloc>& y)
{
  return x.t < y.t;
}

// 如果编译器支持模板函数特化优先级
// 那么将全局的swap实现为使用multimap私有的swap以提高效率
#ifdef __STL_FUNCTION_TMPL_PARTIAL_ORDER

template <class Key, class T, class Compare, class Alloc>
inline void swap(multimap<Key, T, Compare, Alloc>& x,
                 multimap<Key, T, Compare, Alloc>& y)
{
  x.swap(y);
}

#endif /* __STL_FUNCTION_TMPL_PARTIAL_ORDER */

#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32)
#pragma reset woff 1174
#endif

__STL_END_NAMESPACE

#endif /* __SGI_STL_INTERNAL_MULTIMAP_H */

// Local Variables:
// mode:C++
// End:

示例:

遍历 multimap 容器元素
/*    不同于 map 容器,multimap 容器只能采用迭代器的方式,而不能用数组方式,遍历容器的元素。迭代器方式的元素访问,一般要用 begin 和 end 函数找出遍历开始的首元素和结束元素,然后通过迭代器的 “++” 和 “*“ 操作取出元素。
    下面的示例程序,将水果价格元素表的记录打印出来。由于水果价格作为元素的键值,并采用默认的 less<float> 作比较函数对象,将按价格由小到大打印出来。
*/

-------------------------------------------------------- 遍历 multimap 容器元素
#pragma warning(disable:4786)
#include <map>
#include <iostream>
using namespace std;
int main()
{
    multimap<float, char*> mm;
    mm.insert(pair<float, char*>(3.0f, "apple"));
    mm.insert(pair<float, char*>(3.0f, "pear"));
    mm.insert(pair<float, char*>(2.6f, "orange"));
    mm.insert(pair<float, char*>(1.8f, "banana"));
    mm.insert(pair<float, char*>(6.3f, "lichee"));
    // 遍历打印
    multimap<float, char*>::iterator  i, iend;
    iend = mm.end();
    for (i=mm.begin(); i!=iend; ++i)
        cout << (*i).second << '    ' << (*i).first << "元/斤 \n";

    return 0;
}


 

反向遍历 

/*    利用 multimap 容器定义的反向迭代器 reverse_iterator 和 const_reverse_iterator ,及获取反向首尾元素的 rbegin 和 rend 函数,可反向遍历 multimap 容器的元素。
    例如下面使用反向遍历,将水果价格表的记录按价格大小,由大到小打印出来。
    注意:键值相同的 pear 将先于 apple 打印出来。可见,在等键值的反向遍历情形下,后插入的元素先打印出来。
*/

-------------------------------------------------------- 反向遍历 multimap 容器的元素
#pragma warning(disable:4786)
#include <map>
#include <iostream>
using namespace std;
int main()
{
    multimap<float, char*> mm;
    mm.insert(pair<float, char*>(3.0f, "apple"));
    mm.insert(pair<float, char*>(3.0f, "pear"));
    mm.insert(pair<float, char*>(2.6f, "orange"));
    mm.insert(pair<float, char*>(1.8f, "banana"));
    mm.insert(pair<float, char*>(6.3f, "lichee"));
    // 反向遍历打印
    multimap<float, char*>::reverse_iterator  r_i, r_iend;
    r_iend = mm.rend();
    for (r_i=mm.rbegin(); r_i!=r_iend; ++r_i)
        cout << (*r_i).second << '    ' << (*r_i).first << "元/斤 \n";

    return 0;
}


 

multimap 容器的元素的搜索1
/*    由于键值允许重复插入,在 multimap 容器中具有同一个键值的元素有可能不只一个。因此,multimap 容器的 find 函数将返回第一个搜索到的元素位置,如果元素不存在,则返回 end 结束元素位置。equal_range 函数则返回一个可指示相等元素范围区间的 pair 对象。
*/

-------------------------------------------------------- multimap 容器的元素的搜索1
#pragma warning(disable:4786)
#include <map>
#include <iostream>
using namespace std;

// 课程记录结构体
struct CourseRecord
{
    // 课程信息结构体
    struct CourseInfo
    {
        char* course;  // 课程名
        int  period;   // 学时
        char* required; // 必修或选修
    };

    char* teacher;  // 任课教师
    CourseInfo  cf;
    CourseRecord(char* teacher_, char* course_, int period_, char* required_)
    {
        teacher = teacher_;
        cf.course = course_;
        cf.period = period_;
        cf.required = required_;
    }
};

int main()
{
    // 创建 multimap 容器对象 mm
    typedef multimap<char*, CourseRecord::CourseInfo>  coursemmap;
    coursemmap  mm;
    // 插入第1条记录
    CourseRecord  course1 = CourseRecord("张三","操作系统开发",60,"必修");
    pair<char*, CourseRecord::CourseInfo>  pairCourse1(course1.teacher, course1.cf);
    mm.insert(pairCourse1);

    // 插入第2条记录
    CourseRecord  course2 = CourseRecord("李四","编译器开发",30,"必修");
    pair<char*, CourseRecord::CourseInfo>  pairCourse2(course2.teacher, course2.cf);
    mm.insert(pairCourse2);


    // 插入第3条记录
    CourseRecord  course3 = CourseRecord("李四","数据结构",20,"必修");
    pair<char*, CourseRecord::CourseInfo>  pairCourse3(course3.teacher, course3.cf);
    mm.insert(pairCourse3);


    // 插入第4条记录
    CourseRecord  course4 = CourseRecord("李四","Java 开发应用",38,"必修");
    pair<char*, CourseRecord::CourseInfo>  pairCourse4(course4.teacher, course4.cf);
    mm.insert(pairCourse4);

    // 插入第5条记录
    CourseRecord  course5 = CourseRecord("王五","C++程序设计",58,"必修");
    pair<char*, CourseRecord::CourseInfo>  pairCourse5(course5.teacher, course5.cf);
    mm.insert(pairCourse5);

    // 记录搜索
    cout << "搜索<李四老师>的任课记录:\n";
    pair<coursemmap::iterator, coursemmap::iterator>  p = mm.equal_range("李四");
    // 打印结果
    coursemmap::iterator  i;
    for (i=p.first; i!=p.second; ++i)
        cout << (*i).first << '    '
             << (*i).second.course << '    '
             << (*i).second.period << "学时    "
             << (*i).second.required << '    '
             << endl;
    cout << endl << endl;

    return 0;
}


 

multimap 容器的元素的搜索2
-------------------------------------------------------- multimap 容器的元素的搜索2
#pragma warning(disable:4786)
#include <iostream>
#include <string>
#include <UTILITY>
#include <map>
using namespace std;

struct userdevice
{
    string m_devicename;
    long m_deviced;
    int m_devicePopedom;
};

typedef multimap<string, userdevice> USERTABLE;        
typedef USERTABLE::const_iterator CIT;
typedef pair<CIT, CIT> Range;

int main()
{    
    // 定义一个迭代器
    CIT it;
    // 定义4个设备
    userdevice d1, d2, d3, d4;
    d1.m_deviced = 12341234;
    d1.m_devicename = "d1";
    d1.m_devicePopedom = 123;
    
    d2.m_deviced = 23622344;
    d2.m_devicename = "d2";
    d2.m_devicePopedom = 234;
    
    d3.m_deviced = 3451234;
    d3.m_devicename = "d3";
    d3.m_devicePopedom = 345;
    
    d4.m_deviced = 43622344;
    d4.m_devicename = "d4";
    d4.m_devicePopedom = 456;
    
    USERTABLE m_user;
    m_user.insert(pair<string, userdevice>("zhangsanfeng",d1));
    m_user.insert(pair<string, userdevice>("zhangsanfeng",d2));

    m_user.insert(pair<string, userdevice>("zhangsanfeng2",d3));
    m_user.insert(pair<string, userdevice>("zhangsanfeng2",d4));
    
    // 查找方法一(查找key值是"zhangsanfeng")
    Range range = m_user.equal_range("zhangsanfeng");
    for (CIT i = range.first; i!=range.second; ++i)
    {
        cout<< i->second.m_deviced <<','
            << i->second.m_devicename.c_str()<<','
            << i->second.m_devicePopedom
            << endl;
    }
    cout<< endl;
    


    // 查找方法二(查找"zhangsanfeng2")
    CIT it2 = m_user.find("zhangsanfeng2");
    while(it2!=m_user.end())
    {        
        cout<< it2->second.m_deviced <<','
            << it2->second.m_devicename.c_str()<<','
            << it2->second.m_devicePopedom
            << endl;
        ++it2;
    }
    cout<< endl;
    

    // 遍历
    CIT it3 = m_user.begin();
    while (it3!=m_user.end())
    {
        cout<< i->second.m_deviced <<','
            << it3->second.m_devicename.c_str()<<','
            << it3->second.m_devicePopedom
            << endl;
        
        ++it3;
    }
    cout<< endl;

    return 0;
}


 

multimap 容器元素的设计
/*    下面的示例程序调用 multimap 容器的 size 和 count 函数,对元素个数和去某键值的元素个数进行统计
*/

-------------------------------------------------------- multimap 容器元素的设计
#pragma warning(disable:4786)
#include <map>
#include <iostream>
using namespace std;


int main()
{
    multimap<int, char> mm;
    // 打印元素个数:0
    cout << mm.size() << endl;  
    mm.insert(pair<int, char>(3, 'a'));
    mm.insert(pair<int, char>(3, 'c'));
    mm.insert(pair<int, char>(4, 'd'));
    mm.insert(pair<int, char>(5, 'e'));
    // 打印键值为 3 的元素个数
    cout << mm.count(3) << endl;
    // 打印元素个数
    cout << mm.size() << endl;

    return 0;
}



 

你可能感兴趣的:(STL源码剖析 [容器](十七)[stl_multimap.h])