前言

  本文从三个部分去探究HashMap的链表转红黑树的具体时机:

    一、从HashMap中有关“链表转红黑树”阈值的声明;

    二、【重点】解析HashMap.put(K key, V value)的源码;

    三、测试;

一、从HashMap中有关“链表转红黑树”阈值的声明,简单了解HashMap的链表转红黑树的时机

  在 jdk1.8 HashMap底层数据结构:散列表+链表+红黑树(图解+源码)的 “四、问题探究”中,我有稍微提到过散列表后面跟什么数据结构是怎么确定的:

  HashMap中有关“链表转红黑树”阈值的声明:

   /**
* 使用红黑树(而不是链表)来存放元素。当向至少具有这么多节点的链表再添加元素时,链表就将转换为红黑树。
* 该值必须大于2,并且应该至少为8,以便于删除红黑树时转回链表。
*/
static final int TREEIFY_THRESHOLD = 8; /**
* 当桶数组容量小于该值时,优先进行扩容,而不是树化:
*/
static final int MIN_TREEIFY_CAPACITY = 64;

二、【重点】解析HashMap.put(K key, V value)的源码,去弄清楚链表转红黑树的具体时机

  通过查看HashMap的源码可以发现,它的put(K key, V value)方法调用了putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict)来实现元素的新增。所以我们实际要看的是putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict)的源码。

final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
Node<K, V>[] tab;
Node<K, V> p;
int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null); //直接放在散列表上的节点,并没有特意标识其为头节点,其实它就是"链表/红黑树.index(0)"
else {
Node<K, V> e;
K k;
if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K, V>) p).putTreeVal(this, tab, hash, key, value);
       else { //下面的代码是探究“链表转红黑树”的重点:
for (int binCount = 0;; ++binCount) {
if ((e = p.next) == null) { //沿着p节点,找到该桶上的最后一个节点:
p.next = newNode(hash, key, value, null); //直接生成新节点,链在最后一个节点的后面;
//“binCount >= 7”:p从链表.index(0)开始,当binCount == 7时,p.index == 7,newNode.index == 8;
//也就是说,当链表已经有8个节点了,此时再新链上第9个节点,在成功添加了这个新节点之后,立马做链表转红黑树。
if (binCount >= TREEIFY_THRESHOLD - 1)
                treeifyBin(tab, hash); //链表转红黑树 break; 
            }
            ……
            p = e;
          }
       }
       ……
    }
    ……
}

  通过源码解析,我们已经很清楚HashMap是在“当链表已经有8个节点了,此时再新链上第9个节点,在成功添加了这个新节点之后,立马做链表转红黑树”。

三、通过debug,进一步理解链表转红黑树的具体时机

  1. 自定义一个类:该类中去重写hashCode(),让一组数据能得到同样的哈希值,从而实现哈希碰撞。同时也重写equals()方法。

public class A03Bean {
protected int number; public A03Bean(int number) {
this.number = number;
} /**
* 重写hashCode()方法,只要是4的倍数,最后算出的哈希值都会是0.
*/
@Override
public int hashCode() {
return number % 4;
} /**
* 也必须重写equals()方法。当发生哈希冲突的时候,需要调用equals()方法比较两个对象的实际内容是否相同。
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
A03Bean other = (A03Bean) obj;
if (number != other.number)
return false;
return true;
}
}

  2. 将自定义类A03Bean的实例放到HashMap中:

public class A03Method_TreeifyBin2 {
public static void main(String[] args) {
HashMap<A03Bean, Integer> hashMap = new HashMap<>();
hashMap.put(new A03Bean(4), 0);
hashMap.put(new A03Bean(8), 1);
hashMap.put(new A03Bean(12), 2);
hashMap.put(new A03Bean(16), 3);
hashMap.put(new A03Bean(20), 4);
hashMap.put(new A03Bean(24), 5);
hashMap.put(new A03Bean(28), 6);
hashMap.put(new A03Bean(32), 7);
hashMap.put(new A03Bean(36), 8);
hashMap.put(new A03Bean(40), 9);
hashMap.put(new A03Bean(44), 10); System.out.println("hashMap.size = " + hashMap.size()); //查看是否所有对象都放到HashMap中了:
for(A03Bean key : hashMap.keySet()) {
System.out.println(key.number);
}
}
}

  3.debug,断点查看当同一个桶上的链表的长度达到多长时会做“链表转红黑树”的操作。

    断点打在HashMap.putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict)方法的“treeifyBin(tab, hash);”这里。

  4.测试结果:

    当put进第9个元素(hashMap.put(new A03Bean(36), 8);)时,HashMap做了链表转红黑树的操作。

    也就是说:当链表已经有8个元素了,此时put进第9个元素,先完成第9个元素的put,然后立刻做链表转红黑树。这个结论和第2点中得到的结论一致。

    最后的输出结果也证明了所有的元素都成功put进了集合中,hashMap.size等于11。

  到这里,有关“HashMap的链表转红黑树的具体时机”算是解释清楚了,有时间再探究“HashMap的红黑树转回链表的具体时机”。

jdk1.8源码解析:HashMap底层数据结构之链表转红黑树的具体时机的更多相关文章

  1. JDK1.8源码学习-HashMap

    JDK1.8源码学习-HashMap 目录 一.HashMap简介 HashMap 主要用来存放键值对,它是基于哈希表的Map接口实现的,是常用的Java集合之一. 我们都知道在JDK1.8 之前 的 ...

  2. JDK8源码解析 -- HashMap(二)

    在上一篇JDK8源码解析 -- HashMap(一)的博客中关于HashMap的重要知识点已经讲了差不多了,还有一些内容我会在今天这篇博客中说说,同时我也会把一些我不懂的问题抛出来,希望看到我这篇博客 ...

  3. jdk1.8源码解析(1):HashMap源码解析

    jdk1.8 HashMap数据结构 图1-HashMap类图 图2-TreeNode类图 由图1-HashMap类图可知HashMap底层数据结构是由一个Node<K,V>的数组构成.具 ...

  4. jdk1.8源码分析-hashMap

    在Java语言中使用的最多的数据结构大概右两种,第一种是数组,比如Array,ArrayList,第二种链表,比如ArrayLinkedList,基于数组的数据结构特点是查找速度很快,时间复杂度为 O ...

  5. Java源码解析|HashMap的前世今生

    HashMap的前世今生 Java8在Java7的基础上,做了一些改进和优化. 底层数据结构和实现方法上,HashMap几乎重写了一套 所有的集合都新增了函数式的方法,比如说forEach,也新增了很 ...

  6. JDK源码解析---HashMap源码解析

    HashMap简介 HashMap是基于哈希表实现的,每一个元素是一个key-value对,其内部通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长. HashMap是非线程安全的,只是 ...

  7. OpenJDK1.8.0 源码解析————HashMap的实现(一)

    HashMap是Java Collection Framework 的重要成员之一.HashMap是基于哈希表的 Map 接口的实现,此实现提供所有可选的映射操作,映射是以键值对的形式映射:key-v ...

  8. JDK8源码解析 -- HashMap(一)

    最近一直在忙于项目开发的事情,没有时间去学习一些新知识,但用忙里偷闲的时间把jdk8的hashMap源码看完了,也做了详细的笔记,我会把一些重要知识点分享给大家.大家都知道,HashMap类型也是面试 ...

  9. OpenJDK1.8.0 源码解析————HashMap的实现(二)

    上一篇文章介绍了HashMap的一部分的知识,算是为下面HashMap的进一步学习做准备吧. 然后写的时候一直在思考的一个问题是,这方面的知识网上的资料也是一抓一大把,即使是这样我为什么还要花费时间去 ...

随机推荐

  1. 【设计模式】行为型06命令模式(Command Pattern)

    命令模式 个人理解:命令模式,本质上是一种多层次的封装. 好处:降低耦合,扩展极其方便. 以下一段文案摘抄自:http://www.runoob.com/design-pattern/command- ...

  2. JAVA复习笔记03(完)

    31.类中可定义接口 一个定义接口的java文件中最多有1个Public的接口 32.TreeMap 按照键值升序排序 LinkedHashMap 按照插入顺序排序 Map的操作: Map<in ...

  3. OSI参考模型---网络基础篇(1)

    什么是网络 网络就是将分布在不同地理位置,具有独立功能的终端(一切联网的设备都叫终端:例如电脑,手机,智能家电等等联网的设备),通过通信线路(双绞线.光纤.电话线等等)和通信设备(例如:交换机.路由器 ...

  4. MySQL metalock的一些技巧(写大于读的案例,以及获得锁的顺序)

    前言:元数据锁不是锁定数据,而是锁定描述数据的元数据信息.就像很多装修工人(工作线程)在室内(对象上)装修(操作),不能有其他工人(线程)把屋子拆了(表删除了). MySQL 为了数据一致性使用元数据 ...

  5. HDU 6011:Lotus and Characters(贪心)

    http://acm.hdu.edu.cn/showproblem.php?pid=6011 题意:共有n种字符,每种字符有一个val和一个cnt,代表这个字符的价值和数量.可以制造的总价值是:第一个 ...

  6. Flutter学习笔记(5)--Dart运算符

    如需转载,请注明出处:Flutter学习笔记(5)--Dart运算符 先给出一个Dart运算符表,接下来在逐个解释和使用.如下:                            描述       ...

  7. ML.NET技术研究系列-2聚类算法KMeans

    上一篇博文我们介绍了ML.NET 的入门: ML.NET技术研究系列1-入门篇 本文我们继续,研究分享一下聚类算法k-means. 一.k-means算法简介 k-means算法是一种聚类算法,所谓聚 ...

  8. 深入学习Spring框架(三)- AOP面向切面

    1.什么是AOP? AOP为 Aspect Oriented Programming 的缩写,即面向切面编程, 通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术..AOP是OOP的延续, ...

  9. ASP.NET Core Web Api之JWT(一)

    前言 最近沉寂了一段,主要是上半年相当于休息和调整了一段时间,接下来我将开始陆续学习一些新的技术,比如Docker.Jenkins等,都会以生活实例从零开始讲解起,到时一并和大家分享和交流.接下来几节 ...

  10. Bzoj 4582 [Usaco2016 Open] Diamond Collector 题解

    4582: [Usaco2016 Open]Diamond Collector Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 204  Solved: ...