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

14 篇文章 4 订阅

STL源码之unordered_map、unordered_multimap

unordered_map

unordered_map的类定义如下:

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

   }

来看下_Hashtable具体是什么:

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

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

unordered_multimap

unordered_multimap的类定义如下:

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

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

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

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

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

  template<bool _Cache_hash_code, bool _Constant_iterators, bool _Unique_keys>
    struct _Hashtable_traits
    {
      template<bool _Cond>
      using __bool_constant = integral_constant<bool, _Cond>;

      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<iterator, bool>
insert(const value_type& __x)
{ return _M_h.insert(__x); }

// pair 
std::pair<iterator, bool>
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<typename _InputIterator>
void
insert(_InputIterator __first, _InputIterator __last)
{ _M_h.insert(__first, __last); }

// 初始化列表插入
void
insert(initializer_list<value_type> __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 <sys/types.h>  
#include <unistd.h>  
#include <sys/time.h>   
#include <iostream>  
#include <fstream>  
#include <string>  
#include <map>  
#include <ext/hash_map>  
#include <tr1/unordered_map>  
  
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<int,int>、hash_map<int,int>、unordered_map<int,int>  
//typedef map<int,int> MapKey;          //采用map  
//typedef hash_map<int,int> MapKey;     //采用hash_map  
typedef unordered_map<int,int> 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 "<<filename<<" error!"<<endl;  
        return (-1);  
    }  
      
    char buf[1024];  
    char size[100];  
    char unit[100];  
      
    while(fin.getline(buf,sizeof(buf)-1))  
    {  
        if (0 != strncmp(buf,"VmRSS:",6))  
            continue;  
          
        sscanf(buf+6,"%s%s",size,unit);  
          
        memsize = string(size)+string(unit);  
    }  
      
    fin.close();  
      
    return 0;  
}  
  
int main(int argc, char *argv[])  
{  
    struct timeval begin;  
      
    struct timeval end;  
          
    MapKey MyMap;  
      
    gettimeofday(&begin,NULL);  
      
    for(int i=0;i<N;++i)  
        MyMap.insert(make_pair(i,i));  
      
    gettimeofday(&end,NULL);  
      
    cout<<"insert N="<<N<<",cost="<<end.tv_sec-begin.tv_sec + float(end.tv_usec-begin.tv_usec)/1000000<<" sec"<<endl;  
      
    for(int i=0;i<N;++i)  
        MyMap.find(i);  
  
    gettimeofday(&end,NULL);  
      
    cout<<"insert and getall N="<<N<<",cost="<<end.tv_sec-begin.tv_sec + float(end.tv_usec-begin.tv_usec)/1000000<<" sec"<<endl;  
      
    string memsize;  
      
    GetPidMem(getpid(),memsize);  
      
    cout<<memsize<<endl;  
      
    return 0;  
}

记录数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最大

 

 

以上参考:

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值