HashMap的实现

先介绍一些基本的原理。

1.hash函数

  (1)hash函数是一个映像,hash的原意就是杂凑。hash函数的作用就是保证哈希函数值都落在表长允许的范围之内即可。

  (2)对不同的关键字,hash函数可能产生相同的哈希地址。

2.hash函数的构造方法们

哈希函数的构造方法,一是为了得到在表长内的哈希地址,另一个重要作用是保证得到的哈希地址能“均匀地”分布在表中,避免集中。

  (1)  直接定址法

    就是根据给定的关键字,获得一个线性值,作为哈希地址。

      H(key) = a*key+b;  (a,b为常数)

    直接定址法不会产生哈希地址冲突,但是他的地址范围取决于关键字集合,空间利用率低。

  (2)  数字分析法

    适用于关键字是数值类型的,十进制,二进制,十六进制等。

    分析关键字之间的异同点。比如:‘123555123’,’489555741‘,中间三位是相同的,那么就可以取前三位和末三位作为区别这两个关键字的有效数字位置

    将有效位置上的数字相加(舍去进位),对相应的进制数(2、10、16)取模,就可以得到进制内的对应的数字代表着两个关键字。(也可以对有效数字位置上的数字进行其他方式的处理)

  (3)  平方取中法

    同数字分析法,适用于关键字是数值类型的。

    并不是经常碰见有明显异同的关键字,这个时候,每一个位置都是有效位置。

    平方取中的思想是一个数的平方数中间几位跟该数的每一位都有关系,取平方数的中间几位(位数由表长决定)来作为哈希地址。

  (4)  折叠法

     适用于关键字是数值类型的,并且位数很多,并且每一位上的数字分布大致均匀。

     将关键字分割成长度相同的若干个部分,最后一个可以不同,然后相加(舍去进位),得到哈希地址。

  (5)  除留余数法

    这是一种最简单、最常用的方法。

    取关键字被某个不大于哈希表长m的数p除后所得余数作为哈希地址。

    H(key) = key MOD p, (p<=m);

  (6)   随机数法

    比较好理解,就是用关键字作为随机参数,获取随机数作为哈希地址。

    表长不同,取随机数的范围不同。

除直接定址法之外,别的哈希函数构造方法都有可能使不同关键字获得相同哈希地址,这个时候就需要进行冲突避免。处理冲突的方法有下面几种:

  (1)开放定址法

    Hi = ( H(key)+Di ) MOD M;

    其中Di为增量序列,M为哈希表长。

    Di一般有三种取值:

     (1) Di = 1,2,3,...M-1;(线性探测再散列)

     (2) Di = 12 , -12, 22 , -22,32 ,.......k2  (二次探测再散列)

     (3)  Di = 伪随机数序列  (伪随机数探测再散列)

  (2)再哈希法

    Hi = RHi(key)   i = 1,2,3....k

    RHi 是一系列的哈希函数,当产生冲突时候,就顺序选择一个哈希函数进行计算哈希地址,如果仍然冲突,继续选择下一个哈希函数,只到没有哈希冲突为止。

    这种方法能有效避免“聚集”,但是增加了计算时间。

  (3)链地址法

    将所有关键字为同义词(哈希地址相同)的记录存储在同一个链表中,表头存储在长度为M的数组中,表头在数组中的位置,就是哈希地址。

    也就是说如果一个关键字的哈希地址是k(k<m,m为表长),那么就存储在表头在k位置的链表中。

 (4)建立一个公共溢出区

    如果发生冲突,也就是当前的哈希地址已经被占用,则存放在公共溢出区中。此时,哈希函数操作两个表,一个是常规的哈希表,另一个是公共溢出表。

下面介绍HashMap的数据存储实现原理:

(1)选择的哈希算法:

  HashMap存储数据时候,调用put(k,v)方法:

  源代码:

------------------------------------

public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}

-------------------------------------

  其中hash(key)的源代码如下:

  源代码:

-------------------------------------------------------------------------------   

static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

-------------------------------------------------------------------------------

  putVal()的源代码:

-----------------

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;

if ((tab = table) == null || (n = tab.length) == 0)

n = (tab = resize()).length;   -----------------------a-----------------------确定当前的哈希表容量是否不足,如果是就增大为原来的一倍
if ((p = tab[i = (n - 1) & hash]) == null)---------b------------------判断获得的哈希地址是否被占用。对hash值进行取模,(n-1)&hash得到的值肯定小于n,也就是resize后的哈希表长,可见,HashMap使用的哈希函数是除留余数法。
tab[i] = newNode(hash, key, value, null);--------------c------------------新建一个结点node,把新节点加入哈希地址中的链表中(如果哈希地址没被占用,如果占用,则在下面的代码进行处理)
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {  
if ((e = p.next) == null) {  --------------------------d-------------------------如果获得链表的最后一个结点,把新结点接到后面,可见新的结点是在链尾。
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}

---------------------------------------------

可见,HashMap采用的哈希函数是除留余数法,采用过的冲突避免算法是链地址法。

一步一步来,总会变好的。

浅谈HashMap的实现原理相关推荐

  1. 浅谈Rem 及其转换原理

    浅谈Rem 及其转换原理 今天有小伙伴问了我Rem的转换原理,那我就写篇博客记录一下吧! rem 是 CSS3 新增的相对长度单位,是指相对于元素 html 的 font-size 计算值 的大小. ...

  2. 浅谈人工智能的工作原理

    众所周知人工智能现在快速发展,并且为众人所熟知,不仅如此,人工智能也在各行各业中广泛使用.那么人工智能的工作原理是什么呢? 浅谈人工智能的工作原理 人类智能由三个部分构成(还有些其他生物学和科学现象也 ...

  3. 动态磅是怎么原理_浅谈动态地磅的原理及未来发展方向

    浅谈动态地磅的原理及未来发展方向: 文章介绍了动态地磅的结构和工作原理,针对动态地磅的分类做了全面的概述,分别对不同的动态地磅做了对比及详细的阐述,说明选择和使用动态地磅器的注意事项,凸显了轴组式动态 ...

  4. 浅谈HashMap(一)

    小生近来翻看HashMap的源码,颇有些领会.借此机会,聊以共享.小生才疏学浅,文中如有不妥之处,望各位看官不吝赐教.小可拜谢! 本文将从以下几个方面,浅谈HashMap HashMap的数据结构 H ...

  5. 浅谈 git 底层工作原理

    浅谈 git 底层工作原理 系统复习到这里也快差不多了,大概就剩下两三个 sections,这里学习一下 git 的 hashing 和对象. 当然,跳过问题也不大. config 文件 这里还是会用 ...

  6. 伺服驱动器生产文件_浅谈伺服驱动器的工作原理

    原标题:浅谈伺服驱动器的工作原理 目前,主流的伺服驱动器均采用数字信号处理器(DSP)作为控制核心,可以实现比较复杂的控制算法,实现数字化.网络化和智能化.功率器件普遍采用以智能功率模块(IPM)为核 ...

  7. 深入浅谈,CPU设计原理

    首先,声明这是一篇转载文,这篇文章是,从卡饭论坛 看到的一篇文章<深入浅谈,CPU设计原理>,是一篇连载,文章,卡饭论坛,是我高中的时候,经常去的论坛,里面有很多好的文章,推荐给大家.也许 ...

  8. 浅谈代理服务器工作的原理

    浅谈代理服务器工作的原理 (1) 代理服务原理 代理服务器有很多种,大体来说有http,ftp,socks代理三种,其中又分透明代理和不透明代理.其中透明代理一般是网关,是硬件.所以这里讨论不透明代理 ...

  9. 浅谈《微信抢红包原理》

    现在很多人手机可能都安装了抢红包软件,为了过年抢红包不错过,当然会下载来用用,其实,现在的抢红包软件,基本都是通过监听通知栏消息"[微信红包]"字样,作为识别是红包的依据的,可能大 ...

最新文章

  1. mysql数据更新回退_Mysql的几个灵魂拷问(一)
  2. 超全超实用的Javascript类库和jQuery插件大全之一:图片,地图和图形
  3. Learn Blockchains by Building One
  4. 微软出品 Kubernetes 最新学习指南 v3.0
  5. python自动测试n_接口自动化:Jenkins 自动构建python+nose测试
  6. 【高斯消元】球形空间产生器(luogu 4035/金牌导航 高斯消元-1)
  7. 计算机在智慧交通的应用论文,智能交通的毕业论文
  8. Python基础——1基础
  9. Android 最火高速开发框架AndroidAnnotations使用具体解释
  10. hexo sever端口占用,localhost:4000无响应
  11. project实操——项目实例
  12. makefile 指定文件搜索路径和文件生成路径
  13. 深入解析 | 如何设置关键词密度?
  14. 三菱PLC之间通信概要
  15. 阿里巴巴、腾讯投资Barefoot,助力C轮融资8000万美元
  16. 先马后看!详解线性回归、朴素贝叶斯、随机森林在R和Python中的实现应用!(附代码)...
  17. [CF1383A]String Transformation 1
  18. NIR近红外光谱简介
  19. 利用GN算法进行社区发现
  20. 胜任力参考阅读资料汇总

热门文章

  1. Android 开发之 GridView及其事件监听
  2. 记录发现的一个hiredis的bug
  3. 活动目录域结构和域信任关系建立实验
  4. php扩展的xdebug,安装php扩展:Xdebug
  5. php tp5支付宝app支付,支付宝APP支付 统一下单 php服务端 tp5
  6. html转换成keynote,keynote代码高亮【转】
  7. 个性化 服务器运行失败,VirtualBox 运行失败
  8. MySQL常见的主从复制架构_如何搭建经典的MySQL 主从复制架构
  9. 2021重庆黔江中学高考成绩查询,2020年重庆黔江三所高中排名公告
  10. DefaulSqlSession 和SqlSessionTemplate 的区别是什么?