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浅析的更多相关文章

  1. JDK源码学习笔记——HashMap

    Java集合的学习先理清数据结构: 一.属性 //哈希桶,存放链表. 长度是2的N次方,或者初始化时为0. transient Node<K,V>[] table; //最大容量 2的30 ...

  2. Java泛型学习笔记 - (七)浅析泛型中通配符的使用

    一.基本概念:在学习Java泛型的过程中, 通配符是较难理解的一部分. 主要有以下三类:1. 无边界的通配符(Unbounded Wildcards), 就是<?>, 比如List< ...

  3. 【Mysql学习笔记】浅析mysql的binlog

    最近读一份关于“数据库事务故障恢复"的技术资料,发现对mysql的binlog的认识不够清楚,查阅mysql reference manual有所收获,作为笔记,记录于此. 1. What' ...

  4. Java学习笔记--HashMap中使用object做key的问题【转】

    在HashMap中,如果需要使用多个属性组合作为key,可以将这几个属性组合成一个对象作为key.但是存在的问题是,要做get时,往往没办法保存当初put操作时的key object的referenc ...

  5. 【学习笔记】浅析后缀自动机(SAM)及基础应用

    解决子串相关问题的强大工具 我们知道一个长度为 \(n\) 的字符串中所有的子串数目为 \(O(n^2)\) 个,这很大程度上限制了我们对某些子串相关问题的研究.所以有没有解决方案,使得我们可以在可承 ...

  6. 【学习笔记】浅析Promise函数

    一.Promise是什么? 在JavaScript中,所有的代码都是单线程执行,所以javaScript的所有网络操作(“GET”/"POST"/"PUT"/& ...

  7. 【Web学习笔记】浅析CGI概念及用法

    1. CGI是什么         CGI是Common Gateway Interface的简写,它提供了一种标准方法使得位于WebServer后端的web应用可以根据client的请求动态生成网页 ...

  8. 【学习笔记】浅析平衡树套线段树 & 带插入区间K小值

    常见的树套树 一般来说,在嵌套数据结构中,线段树多被作为外层结构使用. 但线段树毕竟是 静态 的结构,导致了一些不便. 下面是一个难以维护的例子: 带插入区间 \(k\) 小值问题 来源:Luogu ...

  9. JDK源码学习笔记——LinkedHashMap

    HashMap有一个问题,就是迭代HashMap的顺序并不是HashMap放置的顺序,也就是无序. LinkedHashMap保证了元素迭代的顺序.该迭代顺序可以是插入顺序或者是访问顺序.通过维护一个 ...

随机推荐

  1. MVC5使用SignalR进行双向通信(1)

    MVC5使用SignalR进行双向通信 (1) 配置Signal 在NuGet中通过 install-package Microsoft.AspNet.SignalR 命令进行安装 在Scripts文 ...

  2. CentOS 6.4安装AMH面板

    复制以下代码 然后执行 或者下载wget http://amysql.com/file/AMH/3.2/amh.sh; chmod 775 amh.sh; ./amh.sh 2>&1 | ...

  3. ASP.NET相关技术整理

  4. SMTP Failed on Process Scheduler

    If you are seeing messages like this in your message log when running a process through the process ...

  5. dataGridView 如何默认选中第一行

    datagridview默认选中第一行方法: this.dataGridView1.Rows[0].Selected = true; datagridview 去除 默认选中第一行方法:在绑定data ...

  6. 在PyQt中直接使用ui文件并加载qrc资源文件

    1. 用Qt设计师创建一个包含qrc资源文件的ui文件 2.打开cmd使用以下命令把qrc资源文件转换成十六进制的py文件 pyrcc4 -o C:\res.py C:\res.qrc pyrcc4 ...

  7. SBM is Not Sale And Run Company

    data = """ Well,We will bet you dollars to donuts,there are so many crusher provider ...

  8. Java中的类加载器----ClassLoader

    1.简单的讲类加载器就是加载类. 在一个类要被执行时,首先会被从硬盘中加载到内存中,这个任务就是由类加载器来完成,如果加载不成功时,类是无法被执行的.类加载器执行的都是字节码二进制文件.   帮助文档 ...

  9. 关于HTML中,绝对定位,相对定位的理解...(学习HTML过程中的小记录)

    关于HTML中,绝对定位,相对定位的理解...(学习HTML过程中的小记录)   作者:王可利(Star·星星) HTML中 相对定位:position:relative; 绝对定位:position ...

  10. SQL基础学习篇--字符函数

    字符函数可与SELECT,UPDATE,DELETE RIGHT()----从右侧开始选择  SELECT RIGHT(列,字符数量) FROM 表 LEFT()----从左侧开始选择  SUBSTR ...