学习笔记--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保证了元素迭代的顺序.该迭代顺序可以是插入顺序或者是访问顺序.通过维护一个 ...
随机推荐
- Face++云相册应用IOS源码
该源码是一个不错的相册应用,Face++云相册应用源码,以人脸识别作为用户注册和登录的依据,登录后可以进入用户的云相册空间,并对相册进行上传图片或删除图片,另添加了分享功能. <ignore_j ...
- atexit注册的函数是在main函数之后执行?
跟atexit函数相识已久,man手册里对atexit的解释是这么一段: The atexit() function registers the given function to be called ...
- Apache与Nginx的优缺点
http://weilei0528.blog.163.com/blog/static/206807046201321810834431 Apache与Nginx的优缺点比较 1.nginx相对于apa ...
- C#使用结构来传递多个参数
当参数超过5个时,建议用结构来传递多个参数. 示例代码如下: public struct MyStruct { public string str; public int number; } clas ...
- IE中console的正确使用方法
本文出处原文链接 转载请注明出处 http://www.cnblogs.com/havedream/p/4519538.html 问题来源:最近在学习easyui,观看的视频教程是孙宇的<EAS ...
- JS模块化工具requirejs教程(一):初识requirejs
随着网站功能逐渐丰富,网页中的js也变得越来越复杂和臃肿,原有通过script标签来导入一个个的js文件这种方式已经不能满足现在互联网开发模式,我们需要团队协作.模块复用.单元测试等等一系列复杂的需求 ...
- js-布尔值
1.任何JavaScript的值都可以转换为布尔值 下面这些将会转换为false(假值): undefined null 0 -0 NaN "" //空字符串 所有其他值,包括所有 ...
- JavaScript 一些基础练习
JavaScript为网页添加动态效果并实现与用户交互的功能.改变颜色以及宽高,隐藏或显示内容,取消设置 <style type="text/css"> body { ...
- sqoop导入数据到hive
1.1hive-import参数 使用--hive-import就可以将数据导入到hive中,但是下面这个命令执行后会报错,报错信息如下: sqoop import --connect jdbc:my ...
- 四、MongoDB的查询
一.MongoDB的下载.安装与部署 二.MongoDB的基础知识简介 三.MongoDB的创建.更新和删除 db.blogs.insert([ { "author": " ...