作为一种最为常用的容器,同时也是效率比较高的容器,HashMap当之无愧。所以自己这次jdk源码学习,就从HashMap开始吧,当然水平有限,有不正确的地方,欢迎指正,促进共同学习进步,就是喜欢程序员这种开源精神。(好吧,第一篇博客有点紧张)
一. HashMap结构
HashMap在jdk1.6版本采用数组+链表的存储方式,但是到1.8版本时采用了数组+链表/红黑树的方式进行存储,有效的提高了查找时间,解决冲突。这里有一篇博客写的非常好,HashMap的结构图也画的非常清楚,鼎力推荐一下杭州Mark的HashMap源码分析。虽然是基于jdk1.6的,但是换成jdk1.8,只要链表长度大于门限时,换成红黑树就好了, 我就不具体解释HashMap的具体结构了。
二.HashMap 定义
继承自AbstractMap,这是一个抽象类,定义了并且实现了Map接口的基本行为,但是基本上HashMap都对其进行了重写覆盖,采用了自己更高效的方式。AbstractMap是我们自己编写Map时,可以用到的基类。(好吧,来自JAVA编程思想,还没有自己写过Map)
实现了Map接口,这里其实有一个疑问,为什么在AbstractMap中已经明显实现Map接口的情况下,还要显著在实现Map接口?这种设计方式的原因?其余都是标记接口。
三.重要的field属性
四.HashMap构造器
HashMap共有四种构造器,常用的有三种,主要分为无参构造器,应用默认的参数;指定初始化容量的构造器;将原有Map装进当前HashMap的构造器。
1.无参构造器
2.含参包括装载因子,与容量
可以看到此时threshold被tableSizeFor()方法重新计算了,那我们研究一下tableSizeFor方法
不得不说写源码的都是追求完美的大神,下面是jdk1.6的实现方式,就是简单的循环,可以看到采用位运算后效率明显提高了
这里可能还有一个疑问,明明给的是初始容量,为什么要计算门限,而不是容量呢?其实这也是jdk1.8的改变,它将table的初始化放入了resize()中,而且压根就没有capacity这个属性,所以这里只能重新计算threshold,而resize()后面就会根据threshold来重新计算capacity,来进行table数组的初始化,然后在重新按照装载因子计算threshold,有点绕,后面看resize()源码就清楚了。
3.仅仅指定容量
4.将已有Map放入当前map中
五.重要的方法
1.Node 内部类
可以看到前面table就是Node数组,Node是什么?它是一个HashMap内部类(改名字了,jdk1.6就叫Entry),继承自 Map.Entry这个内部接口,它就是存储一对映射关系的最小单元,也就是说key,value实际存储在Node中。
2.put方法
这里hashCode的计算也进行了改进,取得key的hashcode后,高16位与低16位异或运算重新计算hash值。
下面重点看一下put方法实现的大拿,putVal方法。
下面在看一下扩容方法resize(),jdk1.8的resize相比以往又多了一份使命,table初始化部分,也会在这里完成。
其实HashMap put方法中最重要的就是putVal和resize()这两个方法了,搞懂他们,就基本搞懂HashMap的存储方式了,因为get方法就是一个反向的过程。
putAll方法就easy了
3.get方法
get方法同样也是最常用的方法(不可能光存不取啊),可以看到通过getNode方法获得节点后,直接取出Node的value属性即可。
相比于插入由于少了扩容的部分,get查找简化了很多,这里也能看到hash算法的精髓,快速定位查找功能,不需要遍历就能查找到。
4.remove方法
这里matchValue这个参数设置很巧妙,巧妙的解决了这个方法的重用问题,当然源码里还有很多很精巧的设计。
5.replace方法
似乎是jdk1.8新增的方法,但是有了前面的理解,这个方法自己写也能做出来。
可以有两种方案,一种是采用putVal的方法,因为很明显putVal是能够替换的,但是这里就涉及到了size,和modCount这两个field的变化了,也是要注意的。
另一种是getNode得到节点,然后替换,所以采用了getNode这个方法。
可以看出jdk1.8考虑了更多的应用场景。
5.其他方法
六.总结
通过分析HashMap源码,可以很好的领会Hash算法的实现原理,以及实现中的各种问题,同时对于链表的操作也是一个非常好的提高过程。
这里也发现,其实这次学习笔记,只是记录了最基本的增删改查,而红黑树,以及entryset,迭代器等等都还没有进行介绍,这也是留在后面有时间要重点介绍的。同时自己的Hash算法基础也要过过关了。
这是第一篇博客,是对自己学习过程的一个记录,也是一个督促。最后再感谢杭州.Mark的那篇博文,对自己分析HashMap有一个非常大的借鉴意义。