HashMap

1.HashMap的默认初始长度是16。

  从key映射到HashMap数组的对应位置,会用到一个Hash函数。为了实现数组中的数据尽量均匀分布,我们通过利用Key的HashCode值来进行某种运算。不是简单的取模运算:index = HashCode(Key)% Length,而是高效的位运算:index = HashCode(Key)&(Length-1)。当默认初始长度取16时,16-1=15的二进制是1111,算法碰撞的概率变小,结果是均匀的。


2.HashMap是数组和链表的结合。

  Put()方法会将新的Entry<K,V>,插入到对应数组桶中链表的头节点,因为HashMap 的发明者认为后插入的Entry被查找的可能性更大。Get()方法会先用HashCode的位运算查找数组中对应的index,再遍历该index中的链表。HashMap的底层实现还是数组,只是数组的每一项都是一条链。


3.HashMap有两个参数影响其性能:①初始容量、②加载因子。

  ①容量:数组的长度。

  ②加载因子:0.75f。数组在其容量增加之前可以达到多满的一种尺度。它是衡量一个散列表的空间使用程度。加载因子越大表示散列表的装填程度越高,对空间的利用越充分,然而查找效率越低;加载因子越小,散列表越稀疏,对空间浪费。一般不修改加载因子。

    当哈希表中条目数超过加载因子与当前容量的乘积时。则对哈希表进行rehash的操作(即重建内部数据结构)。HashMap扩容后,桶的数量是原来的两倍!


4.Entry是HashMap的一个内部类,除了Key和Value外,还有next引用、hash值(用来确定每一个Entry在table数组中的位置)


5.如果Key = null,则调用putForNullKey,这就是为什么HashMap可以使用null作为键的原因。如果Key = null,hash值为0,也就是会插入哈希表的表头table[0]的位置。如果键中存在该Key,则传入的value会覆盖旧的value,同时返回旧的value,这就是为什么HashMap不能有两个相同的key的原因。


6.HashMap的遍历。

  按哈希表的每一个索引的链表从上而下遍历,由于HashMap的存储规则,最晚添加的节点都可能在第一个索引的链表中,这就造成HashMap的遍历是无序的。


7.使用HashMap为什么要重写HashCode()、Equals()?

  HashCode()用于确定唯一的哈希值,例如user可能是根据他的ID;Equals()用于找到对应的桶后,判断链表中数据是否与之相等,例如user相同需要判断他的ID相同。


JDK8中的HashMap

一直到JDK7为止,HashMap的结构都是这么简单,基于一个数组以及多个链表的实现,hash值冲突的时候,就将对应节点以链表的形式存储。

这样子的HashMap性能上就抱有一定疑问,如果说成百上千个节点在hash时发生碰撞,存储一个链表中,那么如果要查找其中一个节点,那就不可避免的花费O(N)的查找时间,这将是多么大的性能损失。这个问题终于在JDK8中得到了解决。再最坏的情况下,链表查找的时间复杂度为O(n),而红黑树一直是O(logn),这样会提高HashMap的效率。

JDK7中HashMap采用的是位桶+链表的方式,即我们常说的散列链表的方式,而JDK8中采用的是位桶+链表/红黑树(有关红黑树请查看红黑树)的方式,也是非线程安全的。当某个位桶的链表的长度达到某个阀值的时候,这个链表就将转换成红黑树。

JDK8中,当同一个hash值的节点数不小于8时,将不再以单链表的形式存储了,会被调整成一颗红黑树(上图中null节点没画)。这就是JDK7与JDK8中HashMap实现的最大区别。

接下来,我们来看下JDK8中HashMap的源码实现。

JDK中Entry的名字变成了Node,原因是和红黑树的实现TreeNode相关联。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值