JDK1.8 HashMap源码笔记

JDK1.8的HashMap在链表过长时引入红黑树以提升查询效率。当链表长度超过8时转换为红黑树,若数组长度小于64则先扩容。转化过程中,先尝试将链表转为TreeNode双向链表,然后按红黑树规则插入。在扩容和移除操作中,对链表和红黑树都有相应处理,确保数据结构的高效。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JDK1.8中的HashMap

在链表存储上进行了优化,当链表结点过多时,查询效率会降低,所以引入了红黑树:在一条链表的长度超过8时,将其转换为红黑树存储,提高查询效率。存储结点使用了尾插法。

HashMap总体与JDK1.7的相似,所以只列出不同之处。

Put方法

依旧是同样的思路,对于key的hashcode进行hash()处理为hash值,再与数组长度-1进行&操作取得下标。但是数组从Entry[]换为了Node[],总体数据结构相同。存储的方式也发生了改变:

当链表长度小于等于8,采用尾插法将结点插入至链表。

当连表长度大于8(第9个结点插入后),将链表转换为红黑树。

链表转为红黑树过程

首先,创建红黑树实际上是一个很复杂的过程,所以,HashMap会优先选择扩容,也就是说,当在转换时数组长度小于64,则会对数组进行一个扩容操作,从而散列化数组达到提速的目的。在转换时数组长度等于64,才会对链表进行红黑树转换。

链表如何转换为红黑树

红黑树结点为TreeNode,继承了Entry,同时拥有parent,left,right,next,prev,hash属性。

在转换时,首先会将整个链表转换为TreeNode双向链表。将prev,next绑定好,再对TreeNode链表进行树化。之后,遍历整个TreeNode双向链表,开始用红黑树的思路进行结点插入。

由于结点大小决定结点存放的位置,遍历树时结点的大小判断遵从如下原则:

  1. 先对比两个结点的hash值,如果hash值相同,则进入2
  2. 获取结点key是否实现了Comparable接口,如果是,则通过compareTo方法对比两个结点的大小。若对比结果为相等,或者key并没有实现Comparable接口,则进入3
  3. 对比两个结点的类名是否相同,如果相同则进入4
  4. 使用System.identityHashCode()方法处理并对比A结点的key的hashcode是否小于等于B结点的key的hashcode;System.identityHashCode()方法是防止开发者重写了类的hashcode()方法导致无法获得类初始化时的hashcode值。它会返回类的hashcode值。

确定位置后,将结点插入,按红黑树的要求使用变色和旋转修改树结构。

扩容

扩容方面与1.7类似,但是添加了树和链表的转换,并且对于链表的转移也使用了全新的机制。

转移链表:

由于扩容的结果为旧容量的2倍,所以,在旧数组n位置上存储的结点只能在新数组的n位置或者n+旧数组容量这两个位置上存放。所以,再判断新数组位置时使用了:key.hash&旧数组容量 + 旧数组下标。并且,先遍历一遍链表,找到所有在原位置的数组(低位),以及所以在原位置+旧数组下标(高位)的链表。并直接将两条链表转移。不再一个一个地去转移。

转移红黑树:

如果结点为TreeNode类型,则此处存储的是红黑树,由于TreeNode类型继承了Node和Entry结点,所以,它不仅仅是树,内部也存在一条TreeNode链表;通过同样的方式找到低位链表和高位链表。在转移低位链表时,先判断高位链表是否为null,如果为null,则表明这个树在新数组都只在一个位置,所以直接将红黑树转移至新数组。如果不满足,则判断链表长度是否小于等于6,如果满足,则利用TreeNode的链表结构将红黑树重新转化为链表存储。如果不满足,则将TreeNode链表重新树化。因为结点的改变会破坏原先的树结构,转移时只是重新组了一条链表,所以需要重新转化为红黑树。

Remove()

remove较为特殊,在remove时,如果是链表,则与JDK1.7大同小异。

如果是红黑树:remove结束之后,先判断是否满足如下条件

  1. 根节点是否为空 ||
  2. 根节点是否有左孩子 ||
  3. 根节点是否有右孩子 ||
  4. 根节点的左孩子是否有左孩子

如果返回的是true,则将红黑树转换为链表,否则,对树结构进行更新。

在remove时,遍历结点的思路与put一致。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值