深入了解HashMap


什么是hash?
哈希算法将任意长度的二进制值映射为较短的固定长度的二进制值,这个小的二进制值称为哈希值。哈希值是一段数据唯一且极其紧凑的数值表示形式。如果散列一段明文而且哪怕只更改该段落的一个字母,随后的哈希都将产生不同的值。要找到散列为同一个值的两个不同的输入,在计算上是不可能的,所以数据的哈希值可以检验数据的完整性。一般用于快速查找和加密算法。

1、任意长度的二进制值
2、映射关系(哈希算法,数学的一些列算法)
3、固定的二进制值(哈希值)

任意长度的二进制值 和 固定长度的二进制值 是一一对应关系
固定长度的二进制值就相当于一个任意长度的二进制值的一个摘要
固定长度的二进制值 相当于一个关键字key

为什么这样做?
举个例子,比如这里有一万首歌,给你一首新的歌X,要求你确认这首歌是否在那一万首歌之内。无疑,将一万首歌一个一个比对非常慢。但如果存在一种方式,能将一万首歌的每首数据浓缩到一个数字(称为哈希码)中,于是得到一万个数字,那么用同样的算法计算新的歌X的编码,看看歌X的编码是否在之前那一万个数字中,就能知道歌X是否在那一万首歌中。

hash表特点:
1、存取效率很高。取数据的时间复杂度为1。
hash表与其他数组如顺序表,链表不同。
顺序表,使用数组实现,一组地址连续的存储单元,数组大小有两种方式指定,一是静态分配,二是动态扩展。
查找的时候,如果知道值的下标,那么查找时间复杂度为1。但多数场景是不知道值的下标的,只能for循环一个个比较,直到找到值位置。因此大多数场景下取值的时间复杂度为n。

链表的定义是递归的,它或者为空null,或者指向另一个节点node的引用,这个节点含有下一个节点或链表的引用。
与顺序存储相比,允许存储空间不连续,插入删除时不需要移动大量的元素,只需修改指针即可,但查找某个元素,只能从头遍历整个链表。查找时间复杂度为n。

hash表
传入一个key,把key代入哈希函数里,通过计算就能得到一个哈希值,而该哈希值就是value的下标,由此找到value值,查找时间复杂度为1。

---以下是转载内容---

原文地址:http://www.iteye.com/topic/539465

 

Hashmap是一种非常常用的、应用广泛的数据类型,最近研究到相关的内容,就正好复习一下。网上关于hashmap的文章很多,但到底是自己学习的总结,就发出来跟大家一起分享,一起讨论。 


1、hashmap的数据结构 
要知道hashmap是什么,首先要搞清楚它的数据结构,在java编程语言中,最基本的结构就是两种,一个是数组,另外一个是模拟指针(引用),所有的数据结构都可以用这两个基本结构来构造的,hashmap也不例外。Hashmap实际上是一个数组和链表的结合体(在数据结构中,一般称之为“链表散列“),请看下图(横排表示数组,纵排表示数组元素【实际上是一个链表】)。
 



从图中我们可以看到一个hashmap就是一个数组结构,当新建一个hashmap的时候,就会初始化一个数组。我们来看看java代码: 

Java代码  收藏代码
  1. /** 
  2.      * The table, resized as necessary. Length MUST Always be a power of two. 
  3.      *  FIXME 这里需要注意这句话,至于原因后面会讲到 
  4.      */  
  5.     transient Entry[] table;  

Java代码  收藏代码
  1. static class Entry<K,V> implements Map.Entry<K,V> {  
  2.         final K key;  
  3.         V value;  
  4.         final int hash;  
  5.         Entry<K,V> next;  
  6. ..........  
  7. }  


        上面的Entry就是数组中的元素,它持有一个指向下一个元素的引用,这就构成了链表。 
       
  当我们往hashmap中put元素的时候,先根据key的hash值得到这个元素在数组中的位置(即下标),然后就可以把这个元素放到对应的位置中了。如果这个元素所在的位子上已经存放有其他元素了,那么在同一个位子上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。从hashmap中get元素时,首先计算key的hashcode,找到数组中对应位置的某一元素,然后通过key的equals方法在对应位置的链表中找到需要的元素。从这里我们可以想象得到,如果每个位置上的链表只有一个元素,那么hashmap的get效率将是最高的,但是理想总是美好的,现实总是有困难需要我们去克服,哈哈~ 

2、hash算法 
我们可以看到在hashmap中要找到某个元素,需要根据key的hash值来求得对应数组中的位置。如何计算这个位置就是hash算法。前面说过hashmap的数据结构是数组和链表的结合,所以我们当然希望这个hashmap里面的元素位置尽量的分布均匀些,尽量使得每个位置上的元素数量只有一个,那么当我们用hash算法求得这个位置的时候,马上就可以知道对应位置的元素就是我们要的,而不用再去遍历链表。 

所以我们首先想到的就是把hashcode对数组长度取模运算,这样一来,元素的分布相对来说是比较均匀的。但是,“模”运算的消耗还是比较大的,能不能找一种更快速,消耗更小的方式那?java中时这样做的,
 

Java代码  收藏代码
  1. static int indexFor(int h, int length) {  
  2.        return h & (length-1);  
  3.    }  


首先算得key得hashcode值,然后跟数组的长度-1做一次“与”运算(&)。看上去很简单,其实比较有玄机。比如数组的长度是2的4次方,那么hashcode就会和2的4次方-1做“与”运算。很多人都有这个疑问,为什么hashmap的数组初始化大小都是2的次方大小时,hashmap的效率最高,我以2的4次方举例,来解释一下为什么数组大小为2的幂时hashmap访问的性能最高。 

         看下图,左边两组是数组长度为16(2的4次方),右边两组是数组长度为15。两组的hashcode均为8和9,但是很明显,当它们和1110“与”的时候,产生了相同的结果,也就是说它们会定位到数组中的同一个位置上去,这就产生了碰撞,8和9会被放到同一个链表上,那么查询的时候就需要遍历这个链表,得到8或者9,这样就降低了查询的效率。同时,我们也可以发现,当数组长度为15的时候,hashcode的值会与14(1110)进行“与”,那么最后一位永远是0,而0001,0011,0101,1001,1011,0111,1101这几个位置永远都不能存放元素了,空间浪费相当大,更糟的是这种情况中,数组可以使用的位置比数组长度小了很多,这意味着进一步增加了碰撞的几率,减慢了查询的效率!
 

 


          所以说,当数组长度为2的n次幂的时候,不同的key算得得index相同的几率较小,那么数据在数组上分布就比较均匀,也就是说碰撞的几率小,相对的,查询的时候就不用遍历某个位置上的链表,这样查询效率也就较高了。 
          说到这里,我们再回头看一下hashmap中默认的数组大小是多少,查看源代码可以得知是16,为什么是16,而不是15,也不是20呢,看到上面annegu的解释之后我们就清楚了吧,显然是因为16是2的整数次幂的原因,在小数据量的情况下16比15和20更能减少key之间的碰撞,而加快查询的效率。 

所以,在存储大容量数据的时候,最好预先指定hashmap的size为2的整数次幂次方。就算不指定的话,也会以大于且最接近指定值大小的2次幂来初始化的,代码如下(HashMap的构造方法中):
 
Java代码  收藏代码
  1. // Find a power of 2 >= initialCapacity  
  2.         int capacity = 1;  
  3.         while (capacity < initialCapacity)   
  4.             capacity <<= 1;  

3、hashmap的resize 

       当hashmap中的元素越来越多的时候,碰撞的几率也就越来越高(因为数组的长度是固定的),所以为了提高查询的效率,就要对hashmap的数组进行扩容,数组扩容这个操作也会出现在ArrayList中,所以这是一个通用的操作,很多人对它的性能表示过怀疑,不过想想我们的“均摊”原理,就释然了,而在hashmap数组扩容之后,最消耗性能的点就出现了:原数组中的数据必须重新计算其在新数组中的位置,并放进去,这就是resize。 

         那么hashmap什么时候进行扩容呢?当hashmap中的元素个数超过数组大小*loadFactor时,就会进行数组扩容,loadFactor的默认值为0.75,也就是说,默认情况下,数组大小为16,那么当hashmap中元素个数超过16*0.75=12的时候,就把数组的大小扩展为2*16=32,即扩大一倍,然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操作,所以如果我们已经预知hashmap中元素的个数,那么预设元素的个数能够有效的提高hashmap的性能。比如说,我们有1000个元素new HashMap(1000), 但是理论上来讲new HashMap(1024)更合适,不过上面annegu已经说过,即使是1000,hashmap也自动会将其设置为1024。 但是new HashMap(1024)还不是更合适的,因为0.75*1000 < 1000, 也就是说为了让0.75 * size > 1000, 我们必须这样new HashMap(2048)才最合适,既考虑了&的问题,也避免了resize的问题。 


4、key的hashcode与equals方法改写 
在第一部分hashmap的数据结构中,annegu就写了get方法的过程:首先计算key的hashcode,找到数组中对应位置的某一元素,然后通过key的equals方法在对应位置的链表中找到需要的元素。所以,hashcode与equals方法对于找到对应元素是两个关键方法。 

Hashmap的key可以是任何类型的对象,例如User这种对象,为了保证两个具有相同属性的user的hashcode相同,我们就需要改写hashcode方法,比方把hashcode值的计算与User对象的id关联起来,那么只要user对象拥有相同id,那么他们的hashcode也能保持一致了,这样就可以找到在hashmap数组中的位置了。如果这个位置上有多个元素,还需要用key的equals方法在对应位置的链表中找到需要的元素,所以只改写了hashcode方法是不够的,equals方法也是需要改写滴~当然啦,按正常思维逻辑,equals方法一般都会根据实际的业务内容来定义,例如根据user对象的id来判断两个user是否相等。 
在改写equals方法的时候,需要满足以下三点: 
(1) 自反性:就是说a.equals(a)必须为true。 
(2) 对称性:就是说a.equals(b)=true的话,b.equals(a)也必须为true。 
(3) 传递性:就是说a.equals(b)=true,并且b.equals(c)=true的话,a.equals(c)也必须为true。 
通过改写key对象的equals和hashcode方法,我们可以将任意的业务对象作为map的key(前提是你确实有这样的需要)。
 

总结: 
        本文主要描述了HashMap的结构,和hashmap中hash函数的实现,以及该实现的特性,同时描述了hashmap中resize带来性能消耗的根本原因,以及将普通的域模型对象作为key的基本要求。尤其是hash函数的实现,可以说是整个HashMap的精髓所在,只有真正理解了这个hash函数,才可以说对HashMap有了一定的理解。
 



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/538225.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

snort入侵检测系统下载Linux,入侵检测系统Snort 2.9.0.2 发布

Snort 是一个免费的、跨平台的软件包&#xff0c;用作监视小型 TCP/IP 网的嗅探器、日志记录、侵入探测器。Snort 是全世界上使用最广泛的入侵预防与侦测软件。Snort 有三种工作模式&#xff1a;嗅探器、数据包记录器、网络入侵检测系统。嗅探器模式仅仅是从网络上读取数据包并…

IRC BOT原来是利用IRC下发CC命令——在xx云环境遇到了,恶意软件开的是6666端口...

Backdoor/IRC.RpcBot 本词条缺少名片图&#xff0c;补充相关内容使词条更完整&#xff0c;还能快速升级&#xff0c;赶紧来编辑吧&#xff01;Backdoor/IRC.RpcBot是一些批处理文件、脚本文件和执行文件的集合&#xff0c;也是一种黑客工具&#xff0c;这些文件的名称是可以变化…

科大奥锐实验报告霍尔效应_大学物理实验报告系列之霍尔效应

【实验名称】霍尔效应【实验目的】1&#xff0e;了解霍尔效应实验原理以及有关霍尔器件对材料要求的知识。2&#xff0e;学习用“对称测量法”消除付效应的影响&#xff0c;测量试样的VH—IS&#xff1b;和VH—IM曲线。3&#xff0e;确定试样的导电类型、载流子浓度以及迁移率。…

Android studio http 代理设置

Android studio http 代理设置 大连东软信息学院镜像服务器地址: - http://mirrors.neusoft.edu.cn 端口&#xff1a;80

三位数倒序数C语言,C语言求助!一个三位数的逆序数,总是编不对

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼#include #include #include int main(){int n,a,b,c,sum,ge,shi,bai;printf("请输入一个三位整数&#xff1a;\n");scanf("%d",&n);nfabs(n);an/100;b(n-a*100)/10;cn%10;if(a>b&&b>c){gec…

DB2 存储过程中执行动态SQL的两种写法

样本代码&#xff1a; DROP PROCEDURE QUOTATION.COPY_SAMPLE; CREATE PROCEDURE QUOTATION.COPY_SAMPLE (IN tableNameFrom VARCHAR(30), IN tableNameTo VARCHAR(30), INOUT copyResult INTEGER)BEGINDECLARE SQLCODE INTEGER DEFAULT 0;SET copyResult 0;-- Proecss 1BEGIN…

tp5 批量更新多条记录_Thinkphp批量更新数据的方法汇总

以下小编给大家列出了三种实现thinkphp批量更新数据的方法,写的不好还请见谅,有意见欢迎提出,共同学习进步! 方法一: //批量修改 data二维数组 field关键字段 参考ci 批量修改函数 传参方式 function batch_update($table_name=,$data=array(),$field=){if(!$table_name||…

linux shmmax单位,Linux核心参数Shmmax,shmall,shmni

Linux 下核心参数调整kernel.shmmaxshmmax是核心参数中最重要的参数之一&#xff0c;用于定义单个共享内存段的最大值&#xff0c;shmmax设置应足够大&#xff0c;能在一个共享内存段下容纳下整个的SGA&#xff0c;设置的过低可能会导致需要创建多个共享内存段&#xff0c;可能…

制作一个App的完整流程是哪些

APP开发流程其实并不复杂&#xff0c;但是对于客户来说&#xff0c;。一般移动APP开发都离不开UI设计师、前端开发、后端开发、测试专员、产品经理等&#xff0c;由于他们的工作性质都不一样&#xff0c;我们且先把APP软件开发项目分为三个阶段&#xff1a;一、功能需求阶段1.功…

Failed to find Build Tools revision 26.0.1

Error:A problem occurred configuring project :app. > Failed to find Build Tools revision 26.0.1 在build.gradle 中buildToolsVersion 如何修改。看本地安装了哪些版本的 进入文件夹Android SDK 目录下build-tools&#xff0c;修改为里面有的版本

netty 游戏服务器框图_基于Netty和WebSocket协议实现Web端自动打印订单服务方法与流程...

本发明涉及电子商务技术领域&#xff0c;尤其涉及一种基于netty和websocket协议实现web端自动打印订单服务方法。背景技术&#xff1a;电子商务是以信息网络技术为手段&#xff0c;以商品交换为中心的商务活动&#xff1b;也可理解为在互联网(internet)、企业内部网(intranet)和…

小学数学动画 android,小学数学动画教学下载-小学数学动画 安卓版v5.0-pc6手机下载...

小学数学动画教学软件是一款能让孩子爱上数学的客户端应用&#xff0c;小学数学动画app以动画的形式带领孩子学习数学知识以及各类公式原理&#xff0c;测底掌握数学方法。功能介绍小学数学动画通过形象、生动、清楚、易懂的触摸动画向你解释小学数学知识和原理(小学数学原理和…

存储芯片在智能化产业链中扮演的角色将更加重要

随着大数据、云计算、物联网等发展&#xff0c;存储芯片作为半导体元器件中不可或缺的组成部分&#xff0c;在内存、消费电子、智能终端等领域均有着非常广泛的应用。近年来&#xff0c;国家把集成电路产业列为“十三五”期间重要的新型战略性产业&#xff0c;国产化“存储芯片…

Tomcat下找不到properties文件

在java core项目里&#xff0c;目录结构如下&#xff1a; 当使用 InputStream ipsnew FileInputStream("config/config.properties");能读到properties文件。但是在java web项目时&#xff0c;部署到Tomcat后。上面的读法就不行了。 javaweb项目结构如下&#xff1a…

win10计算器rsh_Win10 内置计算器评测:PowerShell 很靠谱

计算器几乎是每个操作系统都具备的工具&#xff0c;不管是手机还是电脑&#xff0c;很多人都离不开它。然而这些系统内置计算器标准模式往往功能比较简单&#xff0c;基本上只用于单步运算&#xff0c;就像传统计算器那样&#xff0c;现在的Win10计算器也是如此。不过Windows10…

android tcpdump log分析,android 系统启动过程中加入tcpdump和logcat

一、android 系统启动过程中加入tcpdump &#xff0c;分析开机启动后&#xff0c;系统与服务器端的消息交互。1. init.rc 中的修改1)在init.rc 中加上tcpdump service.service tcpdump /system/xbin/tcpdump -s 0 -w/data/test/test_1.pcapclass core2)在init.rc 中启动tcpdump…

Linux下查看软件安装路径(whereis)

原文链接&#xff1a;http://blog.csdn.net/ly_feng/article/details/7898649----------------------------------------------------------------一、查看文件安装路径&#xff1a;由于初次大部分软件的安装都是系统自动安装的&#xff0c;所有先说查看文件安装的所有路径(地址…

CloudDBA新功能上线--SQL过滤/限制/防火墙

1 前言 CloudDBA是阿里云数据库团队开发的智能诊断和优化平台&#xff0c;可以帮助用户更好使用阿里云数据库。CloudDBA不断提升算法和规则&#xff0c;更好的匹配更多用户场景&#xff0c;刚刚上线了SQL过滤功能&#xff0c;用来解决某类SQL给系统带来的冲击。 2 功能描述 匹配…

js导出的xlsx无法打开_js文件操作之——导出Excel (js-xlsx)

前阵子跟server同学讨论一个Excel导出的需求&#xff0c;我说JS搞不定&#xff0c;需要server来做&#xff0c;被server同学强行打脸。今天研究了下&#xff0c;尼玛&#xff0c;不光可以&#xff0c;还很强大了&#xff01;总结&#xff1a;经验是害人的&#xff0c;尤其是在发…

Linux上Svn环境搭建

一般情况下&#xff0c;Linux都是自带SVN环境的。 查看svn是否安装了 [14:50:28][rootVM60 ~]# rpm -aq subversion [14:50:30]subversion-1.6.11-9.el6_4.x86_64 [14:52:01][rootVM60 ~]# whereis svn [14:52:01]svn: /usr/bin/svn /usr/share/man/man1/svn.1.gz [14:55:…