学习笔记--HashMap浅析
HashMap 实现了Map 接口,其底层以一个线性数组保存哈希表,所以它既有数组查询的高效,也有哈希存取的方便。
HashMap提供了默认构造器,和有参构造器,在有参构造器中,提供了两个参数,可以对集合长度和加载因子自定义。如果不传,默认长度为16,加载因子为0.75,所以实际初始临界长度为16*0.75 = 12。
HashMap 定义了一个内部类Entry<K,V>,从外形上可以看出,HashMap的值实际上就是保存在这个自定义的类上。然后定义了一个transient 的数组,数组类型为Entry。
HashMap在通过put(key,value)设置的时候,首先会拿到key,拿到它的哈希值,key.hashcode(),通过它的一个算法得到新的哈希值

然后通过算法得到value在entry数组中的下标或者叫索引

以下是它的put方法

通过观察可以发现,在存值的时候,首先会对数组进行循环判断,判断它们的KEY和将要存储的KEY的哈希值进行比较,如果相同,则会覆盖掉value,然后把旧的value返回.有意思的是,两个key的hash相同必须同时满足两个条件,即“==”和“equals”同时为true,如果不同时满足这两个条件,但它们KEY的哈希又确实相等,确实有这种情况,即“==”返回true,而“equals”返回false,那么它实际上将不会覆盖,而是在相应的数组节点上,通过entry的next属性形成一个entry链,后进来的排在最前面.
那么这会形成两个问题:1是有的数组节点上形成了一个较长的entry链,而另外的节点上还没有一个值。这大约可以通过增大加载因子来调节;2是数组节点上entry链过长,它会影响查找效率。在加载因子不变的情况下,只有增加长度了。HashMap靠resize()来重设长度,那么,HashMap在什么去增加长度呢?
通过搜索可以发现,在每次put的时候,都会去调用resize()

通过resize()方法可以发现,如果数组超过临界长度,长度会默认增长为当前两倍.那么这就会出现一个新的问题,简单起见,假设原先存储数据的key.hashcode()=5,%12=7,那么这条数据就存在数组下标为7的地方,如果数组长度不够,默认增加一倍就为了24,这时候%24=19,在读取的时候它不是就会跑到数组下标为19的地方去找?考虑到这一点,HashMap会调用一个方法叫做transfer()将原数组取key重新计算在新的数组里的下标。

这个方法会将原先的每条数据遍历,重新计算,然后转移到新的数组。这将会是非常影响效率的操作。
前面讲到数组entry[]table 修饰符为transient,因此,它是不直接包含在序列化里的。HashMap通过迭代器来遍历,它的内部有一个泛型的抽象类HashIterator<E>,同时ValueIterator,KeyIterator,去实现了这个类,在HashMap迭代的时候,通过两个属性来控制方法的同步。一个是modCount,在HashMap的每一个方法,包括put,remove里面,都会看到这个属性,每一次操作它自增。它表示对这个集合更新的次数。假设在迭代之前,modCount为18,然后把值赋给另一属性expectedCount(预期值),如果在迭代操作未完成之前,这个命令被另一个线程进行了一次操作,那么这时modcount=19,它在指向nextEtry即下一个元素之前 ,会去比较两个值,如果不相等,就会抛出异常ConcurrentModificationException().

学习笔记--HashMap浅析的更多相关文章
- JDK源码学习笔记——HashMap
		Java集合的学习先理清数据结构: 一.属性 //哈希桶,存放链表. 长度是2的N次方,或者初始化时为0. transient Node<K,V>[] table; //最大容量 2的30 ... 
- Java泛型学习笔记 - (七)浅析泛型中通配符的使用
		一.基本概念:在学习Java泛型的过程中, 通配符是较难理解的一部分. 主要有以下三类:1. 无边界的通配符(Unbounded Wildcards), 就是<?>, 比如List< ... 
- 【Mysql学习笔记】浅析mysql的binlog
		最近读一份关于“数据库事务故障恢复"的技术资料,发现对mysql的binlog的认识不够清楚,查阅mysql reference manual有所收获,作为笔记,记录于此. 1. What' ... 
- Java学习笔记--HashMap中使用object做key的问题【转】
		在HashMap中,如果需要使用多个属性组合作为key,可以将这几个属性组合成一个对象作为key.但是存在的问题是,要做get时,往往没办法保存当初put操作时的key object的referenc ... 
- 【学习笔记】浅析后缀自动机(SAM)及基础应用
		解决子串相关问题的强大工具 我们知道一个长度为 \(n\) 的字符串中所有的子串数目为 \(O(n^2)\) 个,这很大程度上限制了我们对某些子串相关问题的研究.所以有没有解决方案,使得我们可以在可承 ... 
- 【学习笔记】浅析Promise函数
		一.Promise是什么? 在JavaScript中,所有的代码都是单线程执行,所以javaScript的所有网络操作(“GET”/"POST"/"PUT"/& ... 
- 【Web学习笔记】浅析CGI概念及用法
		1. CGI是什么 CGI是Common Gateway Interface的简写,它提供了一种标准方法使得位于WebServer后端的web应用可以根据client的请求动态生成网页 ... 
- 【学习笔记】浅析平衡树套线段树 & 带插入区间K小值
		常见的树套树 一般来说,在嵌套数据结构中,线段树多被作为外层结构使用. 但线段树毕竟是 静态 的结构,导致了一些不便. 下面是一个难以维护的例子: 带插入区间 \(k\) 小值问题 来源:Luogu ... 
- JDK源码学习笔记——LinkedHashMap
		HashMap有一个问题,就是迭代HashMap的顺序并不是HashMap放置的顺序,也就是无序. LinkedHashMap保证了元素迭代的顺序.该迭代顺序可以是插入顺序或者是访问顺序.通过维护一个 ... 
随机推荐
- linux常见驱动修改
			=============================== 说明 ===============================本文以A5为例,举8种我们公司常用接口的极度精简的驱动程序,只宜参考 ... 
- libharu 源码编译 VS2010
			最近项目中接过了一个libharu PDF 开源库的锅,哈哈.于是就自己编译了一套libharu 的MFC下的静态库和动态库. 编译libharu需要用到zlib库和libpng库,libpng库又依 ... 
- 小米、MIUI、sqlite3: not found--miui安装sqlite3
			以下为miui安装sqlite3的教程: 1.从AVD中将sqlite3导入到PC的D:\android目录下(AVD的版本需要和手机操作系统的版本相同). #adb pull system/xbin ... 
- 在Linux命令行窗口中,怎么向上翻页?
			解决方法:本机环境:vmware linux Redhat9.0版本 使用:Shift + PageUp 和 Shift + PageDown向上和向下翻页 
- datagridview添加复选框全选和取消
			全选 private void All_selected_Click(object sender, EventArgs e) { ; i < this.DataGridViewProduct.R ... 
- Dev的DocumentManager 相关问题
			1.改变DocumentManager包含的窗体的排列方式 if (this.documentManager1.View.Type != ViewType.NativeMdi) { this.docu ... 
- vc++  内存连续读写操作
			//初始化内存 int *data=(int*)malloc(sizeof(int)*4); ZeroMemory(data, sizeof(int)*4); int *m=(int*)malloc( ... 
- C#自定义导出数据到Excel中的类封装
			using System; using System.IO; using System.Data; using System.Collections; using System.Data.OleDb; ... 
- rspec的一些常见用法
			这里讲了如何安装rspec,安装使用rspec. 下面介绍一下rspec中常见的使用方法. 下面是一个最简单的测试用例,判断true是不是等于true,should_be是旧的用法,新用法推荐使用ex ... 
- windows phone版的一个儿教app
			昨天下午看见一个园友写的一篇关于儿教的api,看了也就两三个接口,所以数据处理应该不会太复杂,主要是界面的效果,要求可能比较高.于是我就重新自己写了一个app,实现很简单,花的时间比较多的地方应该是在 ... 
