HashMap的源码浅析
一、HashMap 的数据结构
Java7 及之前主要是“数组+链表”,到了 Java8 之后,就变成了“数组+链表+红黑树”。
二、Java7 源码浅析:
在Java7 中,HashMap 是数据结构里学的 HashTable 经典的实现!
注意点:Java7 中的 HashMap 当我们new出来的时候,他就给我们初始化了底层的 Entry 数组!
1、HashMap()

它自行调用了一个有参的构造器,并传入了默认的数组长度和负载因子:


2、put()
大致流程:先根据 key 的哈希值获取数组索引,然后挨个在数组上查找是否有Entry key 哈希值和值都和 put 的 key 相同,有的话就覆盖 value 了;没有的话,才进行 Entry 的添加!


3、indexFor()
在这里获取数组索引,哈希值 &(数组长度-1),这也就是为什么数组长度为 2 的幂的原因!

4、addEntry()
在添加节点的时候,会进行是否扩容的判断!

三、Java7 中的问题:
- 死锁:头插法。因为复制的时候,依次把链表元素复制到新的数组中去,有可能部分元素获取的数组索引还是相同的,因为头插法会导致链表导致,从而形成环形链表,当CPU下次进入这个链表查询时,产生死锁!
- 安全隐患:Apache Tomcat 底层是使用哈希表来进行存储 Http request 的 parameters,如果此时http请求中的参数中有大量 hash 相同的 key,那么可能导致服务器中形成大量的环形链表,消耗大量CPU,发生Dos!2011年的时候 Tomcat 察觉到并临时想到了一个方法,提供了一个参数,用于限制参数的个数,默认值是 1W!而 Java8 在2014年的时候发布的时候,
四、Java8 的源码浅析:
HashMap()


put()
注意:此时,分配数组索引已经是在判断的时候做的了!

Java 8 相对于 7 的变化:
- new HashMap() 时,底层没有第一时间创建 默认长度的数组!
- 底层是 Node 数组,而非 Entry 数组
- 首次调用 put 方法的时候,才进行数组的创建!(延时加载)
- put 时,如果添加新节点的话,采用的是尾插法!(可避免 7 的死锁问题!)
- 7 的底层结构只有 数组+链表,8 则是 数组+链表+红黑树(当 put 的链表长度等于8,且数组长度等于 64 的时候,这个链表就会转为红黑树结构!)!
浙公网安备 33010602011771号