Java进阶 HashMap原理

jdk7     HashMap实现原理

         数组加链表。先确定数组下标,下标相同再用链表连接。

创建

          在HashMap<> hash = new hashMap()之后,如果使用无参构造器创建,数组默认大小是16,也就是2的四次方。扩容阈值默认是0.75。当数组容量达到   数组长度乘以阈值   , 数组扩容为原来的两倍。
 

put方法的实现过程

        调用put(key,value)方法,  源码进行hash(key)方法,获得一个int k = key.hashCode()。

        再根据    k & (hash.length - 1)  得到一个int数字,也就是数组的下标(事实上没有这么简单,k要经过一系列的  ^   ,    >>   ,    <<  等运算来进行,原因在下文《哈希碰撞》说明)。

        &运算:同位都为1结果为1,否则为0。而且,hash的底层数组长在扩容时,长度都是2的次方。例如,1000  0000,或者0010  0000,这样的意义在于,长度减一之后都是  0111  1111,0001  1111这样的二进制数,再进行与运算得到的结果,一定小于这个二进制数,也就是一定不会数组下标越界。

 

         创建一个Entry对象,里面有四个值,如图。把对象存入hashMap。

         size++。    (hashMap的长度,也就是所有数据的个数)

         put是有返回值的。

             

         运行结果,返回被覆盖掉的value。

          

        put扩容:根据新生成的数组长度重新计算存放位置。

         (在jdk4之前采用取余操作而不是&,但是太慢了,后来改进了。)

哈希碰撞

         实际在确定下标的过程中,很可能出现数组下标被占用的情况,此时下标相同的元素就要用链表来实现存储。后来的元素插入到链表的头部。具体方法自行查看链表的内容。

         上文提到,key经过hashCode()方法得到的值并不是直接与    hash.length()-1   进行&运算,而是经过一系列的异或,位移运算再进行&,为了让k的高位也能参与运算。这样,就能尽可能平均的分布数据,避免链表过长。

null值存放

规定存放在0下标位置。

 

get方法

通过key去计算hashCode,找到数组下标,遍历链表,找到key相等的,返回value。

 jdk8  HashMap实现逻辑

与jdk7的区别:

1.引入红黑树

           在链表长度>=8时把链表结构转换为红黑树结构。  <=6时转换回链表结构。   之所以不完全根据8来转换,是避免频繁的变动来加大开销。   

           注意!并不是链表大于等于8的时候一定会转换红黑树,因为如果数组长度小于64,不会进行树化,而是扩容。因为对于长久眼光来说,此时数据尚少,后续很可能有更多数据传入,总会扩容,而且根据之前提到的扩容方式,又会重新匹配下标,那么此时的树化操作很可能是无用功。总之,树化操作只有在数组长度达到64的时候进行,因为此时如果再通过数组扩充来优化,空间开销就太大了。

2.简化了在计算下标时候,哈希值的异或和位移操作。

            先要明白,jdk7中之所以要进行这么多的操作,是为了尽可能的平均分配数据,避免大量下标相同,导致链表过长。而链表的查询操作是很慢的。

            那jdk8中,引入了红黑树,就是不怕链表过长。大不了用红黑树储存。因此,过多的异或位移操作反而加大了开销。所以简化。

3.在链表尾部添加。

            为什么jdk7中是在链表头部添加而到了jdk就变为了尾部添加呢?

            对链表来说,添加操作的开销主要在于根据nextNode一个一个的查找,而在头部添加,只需要把先添加的对象的next改成原来头部的地址就好了。那么为什么jdk1.8反其道行之呢?

            还是因为红黑树的关系,判断是否对链表进行树化的依据是     数组是否达到预期长度(默认64),      链表长度是否>=8,  因此,需要对链表进行遍历,而遍历的过程,自然而然就拿到了最后一个元素,此时,在尾部添加新对象反而省力。

     

                                                                                      --------------------------如有问题,欢迎指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值