C++进阶——STL源码之unordered_map、unordered_multimap

STL源码之unordered_map、unordered_multimap

unordered_map

unordered_map的类定义如下:

 template,
	   class _Pred = std::equal_to<_Key>,
	   class _Alloc = std::allocator > >
    class unordered_map
    {
      typedef __umap_hashtable<_Key, _Tp, _Hash, _Pred, _Alloc>  _Hashtable;
      _Hashtable _M_h;

   }

来看下_Hashtable具体是什么:

  template
    using __umap_traits = __detail::_Hashtable_traits<_Cache, false, true>;

  template        typename _Tp,
       typename _Hash = hash<_Key>,
       typename _Pred = std::equal_to<_Key>,
       typename _Alloc = std::allocator >,
       typename _Tr = __umap_traits<__cache_default<_Key, _Hash>::value>>
    using __umap_hashtable = _Hashtable<_Key, std::pair,
                                        _Alloc, __detail::_Select1st,
                        _Pred, _Hash,
                        __detail::_Mod_range_hashing,
                        __detail::_Default_ranged_hash,
                        __detail::_Prime_rehash_policy, _Tr>;

unordered_multimap

unordered_multimap的类定义如下:

  template,
	   class _Pred = std::equal_to<_Key>,
	   class _Alloc = std::allocator > >
    class unordered_multimap
    {
      typedef __ummap_hashtable<_Key, _Tp, _Hash, _Pred, _Alloc>  _Hashtable;
      _Hashtable _M_h;
     }

和unordered_map不同的是,unordered_multimap中_Hashtable的类型为__ummap_hashtable:

  template
    using __ummap_traits = __detail::_Hashtable_traits<_Cache, false, false>;

  template        typename _Tp,
       typename _Hash = hash<_Key>,
       typename _Pred = std::equal_to<_Key>,
       typename _Alloc = std::allocator >,
       typename _Tr = __ummap_traits<__cache_default<_Key, _Hash>::value>>
    using __ummap_hashtable = _Hashtable<_Key, std::pair,
                     _Alloc, __detail::_Select1st,
                     _Pred, _Hash,
                     __detail::_Mod_range_hashing,
                     __detail::_Default_ranged_hash,
                     __detail::_Prime_rehash_policy, _Tr>;

通过上述两个类的定义,可以看出不同的地方在于模板参数_Tr不一样,来看下这个_Tr是做什么的,_Hashtable_traits的实现如下:

  template
    struct _Hashtable_traits
    {
      template
      using __bool_constant = integral_constant;

      using __hash_cached = __bool_constant<_Cache_hash_code>;
      using __constant_iterators = __bool_constant<_Constant_iterators>;
      using __unique_keys = __bool_constant<_Unique_keys>;

    };

上述加粗三个的using依次表示:
           __hash_cached:缓存与否,
           __constant_iterators:是否是常迭代器;
           __unique_keys: 是否是唯一的key,

__umap_traits里面有一串__cache_default,实测,__hash_cached为false,表示不缓存hash code。

__umap_traits 和 __ummap_traits 的第二个模板参数都是false,表示迭代器非常迭代器;

__umap_traits 第三个模板参数有为 true,表示key是唯一的;
       __ummap_traits 第三个模板参数有为 false,undered_multimap就是不唯一的key。

主要接口

unordered_map的构造函数:

      explicit
      unordered_map(size_type __n = 10,
            const hasher& __hf = hasher(),
            const key_equal& __eql = key_equal(),
            const allocator_type& __a = allocator_type())
      : _M_h(__n, __hf, __eql, __a)
      { }

hash_map的构造函数:

      hash_map()
      : _M_ht(100, hasher(), key_equal(), allocator_type()) {}

 size and capacity:

      ///  Returns true if the %unordered_map is empty.
      bool
      empty() const noexcept
      { return _M_h.empty(); }

      ///  Returns the size of the %unordered_map.
      size_type
      size() const noexcept
      { return _M_h.size(); }

      ///  Returns the maximum size of the %unordered_map.
      size_type
      max_size() const noexcept
      { return _M_h.max_size(); }

iterator:

  /**
       *  Returns a read/write iterator that points to the first element in the
       *  %unordered_map.
       */
      iterator
      begin() noexcept
      { return _M_h.begin(); }

      //@{
      /**
       *  Returns a read-only (constant) iterator that points to the first
       *  element in the %unordered_map.
       */
      const_iterator
      begin() const noexcept
      { return _M_h.begin(); }

      const_iterator
      cbegin() const noexcept
      { return _M_h.begin(); }
      //@}

      /**
       *  Returns a read/write iterator that points one past the last element in
       *  the %unordered_map.
       */
      iterator
      end() noexcept
      { return _M_h.end(); }

      //@{
      /**
       *  Returns a read-only (constant) iterator that points one past the last
       *  element in the %unordered_map.
       */
      const_iterator
      end() const noexcept
      { return _M_h.end(); }

      const_iterator
      cend() const noexcept
      { return _M_h.end(); }
      //@}

insert的几种形式:

// value
std::pair
insert(const value_type& __x)
{ return _M_h.insert(__x); }

// pair 
std::pair
insert(_Pair&& __x)
{ return _M_h.insert(std::forward<_Pair>(__x)); }

// iterator+value
iterator
insert(const_iterator __hint, const value_type& __x)
{ return _M_h.insert(__hint, __x); }


// first到last范围插入
template
void
insert(_InputIterator __first, _InputIterator __last)
{ _M_h.insert(__first, __last); }

// 初始化列表插入
void
insert(initializer_list __l)
{ _M_h.insert(__l); }

删除:

// iterator
iterator
erase(iterator __position)
{
    return _M_h.erase(__position);
}

// key
size_type
erase(const key_type& __x)
{
    return _M_h.erase(__x);
}

// first到last范围

iterator
erase(const_iterator __first, const_iterator __last)
{
    return _M_h.erase(__first, __last);
}

STL中几种map的区别

map

  内部数据的组织,基于红黑树实现,红黑树具有自动排序的功能,因此map内部所有的数据,在任何时候,都是有序的。

hash_map

hash_map的源码位于(gcc-4.9.0\gcc-4.9.0\libstdc++-v3\include\backward)中,它的底层实现是C:\Users\shado\Desktop\gcc-4.9.0\gcc-4.9.0\libstdc++-v3\include\backward\hashtable.h;和unordered_map的实现还是有点区别的。

  hash_map基于哈希表,数据插入和查找的时间复杂度很低,几乎是常数时间,而代价是消耗比较多的内存。底层实现上,使用一个下标范围比较大的数组来存储元素,形成很多的桶,利用hash函数对key进行映射到不同区域进行保存。

  • 插入操作:得到key -> 通过hash函数得到hash值 -> 得到桶号(hash值对桶数求模) -> 存放key和value在桶内
  • 取值过程:得到key -> 通过hash函数得到hash值 -> 得到桶号(hash值对桶数求模) -> 比较桶内元素与key是否相等 -> 取出相等纪录的value
  • 当每个桶内只有一个元素时,查找时只进行一次比较,当很多桶都没有值时,查询更快。
  • 用户可以指定自己的hash函数与比较函数。

unordered_map

  C++ 11标准中加入了unordered系列的容器。unordered_map记录元素的hash值,根据hash值判断元素是否相同。其底层的实现为C++进阶——STL源码之hashtable。unordered_map的取值和插入操作和hash_map类似。

三者对比:

以下测试来源于:https://www.cnblogs.com/ranjiewen/p/5328137.html

/** 
比较map、hash_map和unordered_map的执行效率以及内存占用情况 
**/  
  
#include   
#include   
#include    
#include   
#include   
#include   
#include   
#include   
#include   
  
using namespace std;  
  
using namespace __gnu_cxx;  
  
using namespace std::tr1;  
  
#define N 100000000  //分别测试N=100,000、N=1,000,000、N=10,000,000以及N=100,000,000  
  
//分别定义MapKey=map、hash_map、unordered_map  
//typedef map MapKey;          //采用map  
//typedef hash_map MapKey;     //采用hash_map  
typedef unordered_map MapKey;  //采用unordered_map  
  
  
  
int GetPidMem(pid_t pid,string& memsize)  
{  
    char filename[1024];  
      
    snprintf(filename,sizeof(filename),"/proc/%d/status",pid);  
      
    ifstream fin;  
      
    fin.open(filename,ios::in);  
    if (! fin.is_open())  
    {  
        cout<<"open "<

记录数N=100000时,结果如下:

Map类型

插入耗时,单位秒

插入加遍历耗时,单位秒

内存占用情况

map

0.110705

0.171859

5,780kB

hash_map

0.079074

0.091751

5,760kB

unordered_map

0.041311

0.050298

5,216kB

记录数N=1000000时,结果如下:

Map类型

插入耗时,单位秒

插入加遍历耗时,单位秒

内存占用情况

map

1.22678

1.95435

47,960kB

hash_map

0.684772

0.814646

44,632kB

unordered_map

0.311155

0.386898

40,604kB

记录数N=10000000时,结果如下:

Map类型

插入耗时,单位秒

插入加遍历耗时,单位秒

内存占用情况

map

14.9517

23.9928

469,844kB

hash_map

5.93318

7.18117

411,904kB

unordered_map

3.64201

4.43355

453,920kB

记录数N=100000000时,结果如下:

Map类型

插入耗时,单位秒

插入加遍历耗时,单位秒

内存占用情况

map

167.941

251.591

4,688,692kB

hash_map

46.3518

57.6972

3,912,632kB

unordered_map

28.359

35.122

4,3012,56kB

unordered_map的效率都优于hash_map,更优于map;而空间复杂度方面,hash_map最低,unordered_map次之,map最大

 

 

以上参考:

  • STL源码剖析
  • https://www.cnblogs.com/ranjiewen/p/5328137.html

 

你可能感兴趣的:(STL,C/C++)