深入了解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;嗅探器、数据包记录器、网络入侵检测系统。嗅探器模式仅仅是从网络上读取数据包并…

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

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

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;修改为里面有的版本

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

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

Tomcat下找不到properties文件

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

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 功能描述 匹配…

android主流技术框架,android开发现在流行什么IDE和开发框架?

慕仙森idea, AS (android studio), adt, 其中 AS 是google 非常推荐的.看官网就知道了. 框架的话: xutils , andbase , volley等等,还有比较流行的UI界面效果,个性化控件等等.  关于 IDE 的优劣势. 个人认为:各有各的好处.  对于以前 Java 的开发人员来说(我就是的,嘿嘿) ,…

联想拯救者y7000加内存条_关于2020款联想拯救者Y7000、R7000和Y7000P,r7000p选哪个好?看这里就对了...

朋友咨询&#xff1a;作为传媒行业&#xff0c;经常用到ps&#xff0c;pr&#xff0c;lr&#xff0c;ae&#xff0c;au&#xff0c;flash&#xff0c;3d max&#xff0c;edius等这些软件&#xff0c;8000以内的预算&#xff0c;那么2020款联想拯救者Y7000、R7000和Y7000P&#…

android 释放bitmap fragment,,为什么重复Replace Fragment会内存泄漏

如图&#xff0c;当点击下面的两个按钮时&#xff0c;REPALCE上面的Fragmnet&#xff0c;共两个&#xff0c;反复切换时发生了内存泄漏这是Fragment的代码&#xff1a;public class Fragment2 extends Fragment {private List lb new ArrayList<>();NullableOverridepub…

Improved GAN

https://www.bilibili.com/video/av9770302/?p16 从之前讲的basic gan延伸到unified framework&#xff0c;到WGAN 再到通过WGAN进行Generation和Transformation 复习一下GAN&#xff0c; 首先我们有一个目标&#xff0c;target分布&#xff0c;Pdata&#xff0c; 蓝色部分表示…

机器学习:样本集、验证集(开发集)、测试集

样本集、验证集&#xff08;开发集&#xff09;、测试集。 Ripley, B.D&#xff08;1996&#xff09;在他的经典专著Pattern Recognition and Neural Networks中给出了这三个词的定义。 Training set: A set of examples used for learning, which is to fit the parameters [i…

Linux ftp传送问题 WARNING! 258831 bare linefeeds received in ASCII mode

WARNING! 258831 bare linefeeds received in ASCII mode 原因是传输时ftp的传输类型不一致。 在上传的时候&#xff0c;选择传输类型为 二进制 在Linux上用ftp命令get的时候&#xff0c;也要设置二进制 ftp> binary // 设置传输方式为binary

html输入支付密码样式,基于JS实现类似支付宝支付密码输入框

基于JS实现类似支付宝支付密码输入框2019-01-06编程之家https://www.jb51.cc编程之家收集整理的这篇文章主要介绍了基于JS实现类似支付宝支付密码输入框&#xff0c;编程之家小编觉得挺不错的&#xff0c;现在分享给大家&#xff0c;也给大家做个参考。本文实现的是一个类似支付…

海康+萤石云+云存储多少钱一个月_400万极清画质 萤石C6Wi智能家居摄像机

作为全球最大安防企业海康威视旗下子品牌&#xff0c;萤石以“家庭安防”切入智能家居市场&#xff0c;萤石C6Wi作为一款融合目前市面上云台摄像头最新设计和最全功能的产品&#xff0c;其外观设计获得多项国际大奖&#xff0c;支持2K分辨率(25601440)及4倍变焦拍摄、微光全彩、…

Vue+axios统一接口管理

通过axios请求接口已经很简单了&#xff0c;但最近在做一个vue项目&#xff0c;想着把axios请求再封装一下&#xff0c;这样api就可以只在一处配置成方法&#xff0c;在使用的时候直接调用这个方法。 但咱们不用每个接口都定义成一个啰嗦的axios请求方法&#xff0c;既然是想简…

html网页 table布局实例,HTML用Table表格对网页布局

HTML是用于开发网页的“超文本标记语言”&#xff0c;今天我们一起来学习一下HTMLCSS网页布局中Table布局方式。常见的网页布局用CSS而言一般有经典行布局、经典列布局、双飞翼布局、圣杯布局等。今天小编教大家用Table表格布局。大家先来欣赏几个网页&#xff1a;这几个网页布…

新年快乐轮播特效html,基于owl-carousel的卡片水平轮播展示特效

这是一款基于owl-carousel的卡片水平轮播展示特效。该卡片轮播展示特效可以通过前后导航按钮来切换卡片&#xff0c;它是响应式设计&#xff0c;在手机等小屏幕设备上&#xff0c;会自动调节为只展示一个卡片。使用方法在页面中引入bootstrap.css和style.css文件&#xff0c;以…

PC,移动端H5实现实现小球加入购物车效果

HTML部分&#xff1a; <!DOCTYPE html> <html> <head><meta http-equiv"content-type" content"text/html; charsetUTF-8"><title>基于jquery.fly模仿天猫抛物线加入购物车特效代码</title><style>* {margin: 0…

云桌面部署_云桌面时代降临-青椒云工作站

云计算理念是当代互联网时代的新型理念&#xff0c;用户享受的所有资源、所有应用程序全部都由一个存储和运算能力超强的云端后台来提供。云桌面是基于云计算技术&#xff0c;实现各种终端设备之间的互联互通。我们的电子设备等都只是一个单纯的显示和操作终端&#xff0c;它们…