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. CentOS6.5下安装配置MySQL

    CentOS6.5下安装配置MySQL,配置方法如下: 安装mysql数据库:# yum install -y mysql-server mysql mysql-deve 查看mysql-server ...

  2. CentOS6.5 python 2.6升级到2.7

    在CentOS6.5下,将自带的python2.6.6升级到python2.7.3,解决方法如下: 下载python2.7.3包,并解压缩,输入命令:#wget http://python.org/f ...

  3. app打包流程

    1.什么是打包 将应用程序统一放在一个后缀是ipa的文件中,然后发给其他人,可以安装在手机上供用户或测试人员安装 2.可安装ipa的前提 ①说清楚是哪一个应用程序(App Id) ②可以安装在哪一台设 ...

  4. MyEclipse中使用debug调试程序

    最基本的操作是:       1.首先在一个java文件中设断点,然后debug as-->open debug Dialog,然后在对话框中选类后--> Run       当程序走到断 ...

  5. Connect to a Windows PC from Ubuntu via Remote Desktop Connection

    http://www.7tutorials.com/connecting-windows-remote-desktop-ubuntu A useful feature of Windows is be ...

  6. POJ C++程序设计 编程题#1 大整数的加减乘除

    编程题#4:大整数的加减乘除 来源: POJ (Coursera声明:在POJ上完成的习题将不会计入Coursera的最后成绩.) 注意: 总时间限制: 1000ms 内存限制: 65536kB 描述 ...

  7. varnish状态引擎1

    vcl: state engine:各引擎之间存一定程度上的相关性:前一个engine如果可以有多种下游engine,则上游engine需要用return指明 要转移的下游engine vcl_rec ...

  8. Laravel 安装predis 扩展

    在安装predis扩展之前先安装composer,安装教程在https://getcomposer.org/download/: php -r "copy('https://getcompo ...

  9. laravel5.2 学习之服务提供者

    契约接口:app\Contracts\LanguageContract.php <?php namespace App\Contracts; interface LanguageContract ...

  10. Android开发环境下关于如何导出手机通讯录数据库【Written By KillerLegend】

    首先度Linux中的权限(Permissions)进行一些说明: permissions一共有10个符号位,[- --- --- ---],在这里我们从左至右由0开始编号,各个符号位的编号分别为0,1 ...