unordered_map, hash_map, map 区别_hashmap和unorderedmap区别-程序员宅基地

技术标签: 数据结构  

1. unordered_map, hash_map, map 概述

C++中,map(来自于 STL) ,底层实现采用红黑树。

hash_map(有很多种实现,底层实现均采用hashtable。目前普遍使用的来自 SGI 的 STL),还未成为C++标准,不过,在可预见的将来,会成为C++标准。

unordered_map 实现来自于 boost 库,底层实现也是hashtable。


2. hash_map的实现

map的实现我们在此不表,谈谈hash_map的实现,本文中以SGI的hash_map为例子进行说明

(1)hash_map原理


hash_map使用hash table来实现,首先分配内存,形成许多bucket用来存放元素,然后利用hash函数,对元素的key进行映射,存放到对应的bucket内。这其中hash函数用于定址,额外的比较函数用于解决冲突。该过程可以描述为:

a.计算元素的key

b.通过hash函数对key进行映射(常见的为取模),得到hash值,即为对应的bucket索引

c.存放元素的key和data在bucket内。

对应的查询过程是:

a.计算元素的key

b.通过hash函数对key进行映射(常见的为取模),得到hash值,即为对应的bucket索引

c.比较bucket内元素的key’与该key相等,若不相等则没有找到。

d.若相等则取出该元素的data。

所以实现hash_map最重要的两个东西就是hash函数和比较函数。以下以SGI的hash_map为例子进行说明。

(2)hash_map类定义

map构造时只需要比较函数(小于函数),hash_map构造时需要定义hash函数和比较函数(等于函数)。SGI中hash_map定义于stl_hash_map.h,定义为:


// Forward declaration of equality operator; needed for friend declaration.  
  
template <class _Key, class _Tp,  
          class _HashFcn  __STL_DEPENDENT_DEFAULT_TMPL(hash<_Key>),  
          class _EqualKey __STL_DEPENDENT_DEFAULT_TMPL(equal_to<_Key>),  
          class _Alloc =  __STL_DEFAULT_ALLOCATOR(_Tp) >  
class hash_map;  
  
......  
  
template <class _Key, class _Tp, class _HashFcn, class _EqualKey,  
          class _Alloc>  
class hash_map  
{  
......  
}  

其中,参数1和参数2分别为键和值,参数3和参数4分别为hash函数和比较函数,实际上STL中使用结构体来封装这两个函数,用户可以自定义这两个结构体,也可以采用提供的默认值。参数5是hash_map的allocator,用于内部内存管理。


3. 三者区别杂记

为啥hash_map如此消耗我们的内存

使用过hash_map,hash_set的都知道,他们耗内存很严重,比如1个1亿{key(8B),value(8B)}的hash_map,理论计算内存为0.1G*(8+8)=1.6G,但实际内存却要4G(64位机),1.6G vs 4G,坑爹啊!!!hash_set内存消耗也一样需要4G,而理论上只需要0.1G*8=800M。它都拿去干嘛了?
[Node*(8B)] -> [Node(nB)] -> [Node(nB)] ......
......
......
[Node*(8B)] -> [Node(nB)] -> [Node(nB)] ......
hash_map实现简图如上。它使用hash_table实现,需要维护一个hash桶 ,桶里面存一个Node*指针(8B),为了性能考虑,这个桶的size被限定在>=节点数。也就是说1个1亿key的hash_map,桶就要大于1亿。Node*指向一个节点链表,hash到同一key的节点都挂载这个链表后面。每来一个新的key就new一个Node。Node里面存的是{key(8B), value(8B), Node*(8B)}。这样算下来内存应该是0.1G*(8B+8B+8B+8B)=3.2G。那么为啥还不到4G呢?
很不幸,linux每new一块内存,都要在头部用8B来记录后面new的buffer长度。而且,new的buffer最少要大于等于32B,且为16B的整数倍!也就是说如果你new一个1B的char,那么内存也得要32B(实际测试是33B)!于是内存就应该是0.1G*(8B+32B)=4.0G。
对于hash_set,Node是{key(8B), Node*(8B)},这同样要耗32B。因而内存消耗同样是0.1G*(8B+32B)=4.0G。这就是为啥hash_set居然跟hash_map耗内存一样的原因(前提是key,value加在一起小于16B)。


unordered_map的初始化比较耗时,我们都知道map是红黑树,unordered_map是哈希表,造成性能差异的原因在于,红黑树初始化时,节点只需要一个,后续的插入只是插入新的节点,但是哈希表初始化时就不是那么简单了,哈希表初始化时需要申请一个数组,数组的每个元素都指向一条链表,所以初始化时需要申请很多内存,相比于map,的确更耗时。

相对于平衡树,hashtable的优点:

插入,查找,删除复杂度为常数时间,大规模查询时,性能差距更为明显。


其实相比于hash,平衡树也是有优点的:

1. 尽管我们都说hash查找插入删除复杂度是常数时间,但这仅仅是个统计上的概念,最差情况下,也是会达到O(n),而红黑树,最差的情况下也是O(logn)

2.hash实际上是空间换时间的做法,空间越小,操作的时间复杂度越大,操作时间越不稳定,而平衡树则稳定很多

3.还有一个就是序,红黑树是查找树,因此中序遍历的结果就是排好序的。这就使得其在范围查找方面性能优秀,而hash却需要遍历全部数据,之后统计才能得出范围查找的结果。另外,如果你知道一个元素在树中的位置,和它大小相近的元素也在它周围,这就使得获取相近元素的时间很少。另外,我们知道,中序遍历的时间复杂度也只是O(n),这个性质非常有用。

至于unordered_map,原理和hash_map类似,但其在某些方面做了优化,性能好于hash_map,所以,业界有一个建议,如果不是为了有序,尽量使用unordered_map。





版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u010982765/article/details/79759928

智能推荐

基于Kepler.gl 和 Google Earth Engine 卫星数据创建拉伸多边形地图-程序员宅基地

文章浏览阅读965次,点赞18次,收藏21次。现在我们有了 2021 年和 2023 年的 NDVI 数据帧,我们需要从 2021 年的值中减去 2023 年的值以捕获 NDVI 的差异。该数据集包括像素级别的植被值,我们将编写一个自定义函数来根据红色和绿色波段的表面反射率计算 NDVI。在我的上一篇文章中,我演示了如何将单个多边形分割/镶嵌为一组大小均匀的六边形。现在我们有了植被损失数据,让我们使用 Kepler.gl 可视化每个六边形的植被损失。将地图保存为 HTML 文件,在浏览器中打开 HTML 以获得更好的视图。现在我们将调用该函数并使用、

Echarts绘制任意数据的正态分布图_echarts正态分布图-程序员宅基地

文章浏览阅读3.3k次,点赞6次,收藏5次。正态分布,又称高斯分布或钟形曲线,是统计学中最为重要和常用的分布之一。_echarts正态分布图

Android中发送短信等普通方法_android bundle.get("pdus");-程序员宅基地

文章浏览阅读217次。首先要在Mainfest.xml中加入所需要的权限:[html] view plain copyprint?uses-permission android:name="android.permission.SEND_SMS"/> uses-permission android:name="android.permission.READ_SMS"/> _android bundle.get("pdus");

2021-07-26 WSL2 的安装和联网_wsl2 联网-程序员宅基地

文章浏览阅读2.6k次。0、说明最近在学习 Data Assimilation Research Testbed (DART) 相关内容,其软件是在 Unix/Linux 操作系统下编译和运行的 ,由于我的电脑是 Windows 10 的,DART 推荐可以使用 Windows Subsystem For Linux (WSL) 来创建一个 Windows 下的 Linux 子系统。以下的内容主要介绍如何安装 WSL2,以及 WSL2 的联网。1、如何在 Windows 10 下安装WSL具体的安装流程可以在 microso_wsl2 联网

DATABASE_LINK 数据库连接_添加 database link重复的数据库链接命-程序员宅基地

文章浏览阅读1k次。DB_LINK 介绍在本机数据库orcl上创建了一个prod_link的publicdblink(使用远程主机的scott用户连接),则用sqlplus连接到本机数据库,执行select * from scott.emp@prod_link即可以将远程数据库上的scott用户下的emp表中的数据获取到。也可以在本地建一个同义词来指向scott.emp@prod_link,这样取值就方便多了..._添加 database link重复的数据库链接命

云-腾讯云-实时音视频:实时音视频(TRTC)-程序员宅基地

文章浏览阅读3.1k次。ylbtech-云-腾讯云-实时音视频:实时音视频(TRTC)支持跨终端、全平台之间互通,从零开始快速搭建实时音视频通信平台1.返回顶部 1、腾讯实时音视频(Tencent Real-Time Communication,TRTC)拥有QQ十几年来在音视频技术上的积累,致力于帮助企业快速搭建低成本、高品质音视频通讯能力的完整解决方案。..._腾讯实时音视频 分享链接

随便推点

用c语言写个日历表_农历库c语言-程序员宅基地

文章浏览阅读534次,点赞10次,收藏8次。编写一个完整的日历表需要处理许多细节,包括公历和农历之间的转换、节气、闰年等。运行程序后,会输出指定年份的日历表。注意,这个程序只是一个简单的示例,还有很多可以改进和扩展的地方,例如添加节气、节日等。_农历库c语言

FL Studio21.1.1.3750中文破解百度网盘下载地址含Crack补丁_fl studio 21 注册机-程序员宅基地

文章浏览阅读1w次,点赞28次,收藏27次。FL Studio21.1.1.3750中文破解版是最优秀、最繁荣的数字音频工作站 (DAW) 之一,日新月异。它是一款录音机和编辑器,可让您不惜一切代价制作精美的音乐作品并保存精彩的活动画廊。为方便用户,FL Studio 21提供三种不同的版本——Fruity 版、Producer 版和签名版。所有这些版本都是独一无二的,同样具有竞争力。用户可以根据自己的需要选择其中任何一种。FL Studio21.1.1.3750中文版可以说是一站式综合音乐制作单位,可以让您录制、作曲、混音和编辑音乐。_fl studio 21 注册机

冯.诺伊曼体系结构的计算机工作原理是,冯 诺依曼型计算机的工作原理是什么...-程序员宅基地

文章浏览阅读1.3k次。冯诺依曼计算机工作原理冯 诺依曼计算机工作原理的核心是 和 程序控制世界上不同型号的计算机,就其工作原理而言,一般都是认为冯 诺依曼提出了什么原理冯 诺依曼原理中,计算机硬件系统由那五大部分组成的 急急急急急急急急急急急急急急急急急急急急急急冯诺依曼结构计算机工作原理的核心冯诺依曼结构和现代计算机结构模型 转载重学计算机组成原理 一 冯 诺依曼体系结构从冯.诺依曼的存储程序工作原理及计算机的组成来..._简述冯诺依曼计算机结构及工作原理

四国军棋引擎开发(2)简单的事件驱动模型下棋-程序员宅基地

文章浏览阅读559次。这次在随机乱下的基础上加上了一些简单的处理,如进营、炸棋、吃子等功能,在和敌方棋子产生碰撞之后会获取敌方棋子大小的一些信息,目前采用的是事件驱动模型,当下完一步棋界面返回结果后会判断是否触发了相关事件,有事件发生则处理相关事件,没有事件发生则仍然是随机下棋。1.事件驱动模型首先定义一个各种事件的枚举变量,目前的事件有工兵吃子,摸暗棋,进营,明确吃子,炸棋。定义如下:enum MoveE..._军棋引擎

STL与泛型编程-第一周笔记-Geekband-程序员宅基地

文章浏览阅读85次。1, 模板观念与函数模板简单模板: template< typename T > T Function( T a, T b) {… }类模板: template struct Object{……….}; 函数模板 template< class T> inline T Function( T a, T b){……} 不可以使用不同型别的..._geekband 讲义

vb.net正则表达式html,VB.Net常用的正则表达式(实例)-程序员宅基地

文章浏览阅读158次。"^\d+$"  //非负整数(正整数 + 0)"^[0-9]*[1-9][0-9]*$"  //正整数"^((-\d+)|(0+))$"  //非正整数(负整数 + 0)"^-[0-9]*[1-9][0-9]*$"  //负整数"^-?\d+$"    //整数"^\d+(\.\d+)?$"  //非负浮点数(正浮点数 + 0)"^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0..._vb.net 正则表达式 取html中的herf