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最大。
以上参考: