1 缘起

补充树相关的知识过程中,学习到红黑树,并查了一些红黑树的应用,
发现Java提供的数据工具包:HashMap中使用了红黑树,
之前在应对知识考核时也看了HashMap的相关数据组成,
但是,没有详细分析数据之间是如何转换存储结构的,
现在,正好借着学习树以及应用的机会,根据HashMap源码,
详细研究了HashMap的数据转换过程,
分享如下,
帮助读者轻松应对知识考核与交流。

2 分析

2.1 理论:回顾HashMap

HashMap是一个组合的数据结构。
根据自己动手丰衣足食的设计原则,当某种开发语言不支持更加高效的数据结构存储数据时,
开发者可根据实际需要,编写自己的数据结构,
这不,Java工具包开发者组合了数组、链表和红黑树,创造了HashMap,
集相对高效的CURD于一身。
老规矩,根据理论,
先看看HashMap的数据结构,如下图所示,
由图可知,HashMap默认的数组元素有16个,
数组中的元素存储情况:
(1)key没有Hash冲突,则按照kv形式存储,如key2对应的形式;
(2)key有Hash冲突且冲突的key个数小于7个,使用链表存储,如key1对应的形式;
(3)key有Hash冲突且冲突的key个数大于等于7个,使用红黑树存储,如key3对应的形式。

hashmap转换treemap hashmap转换成数组_单向链表

2.2 实践:新增数据

使用HashMap存储数据,暴露给用户的方法为put,源码如下图所示,

由源码可知,put方法中,调用了putVal方法,这个方法才是实际的存储数据和数据结构转换的方法。

位置:java.util.HashMap#put

hashmap转换treemap hashmap转换成数组_hashmap转换treemap_02

既然putVal才是最终的奥义,
那么继续看看这个方法的真实面目,如下图所示,
新增数据,
我已经将数据的类型和数据结构的变化在图中标出,
各位习者耐心看下,设计非常巧妙,
Node<>[]即数组;
p.next即链表,
treeifyBin即红黑树。

位置:java.util.HashMap#putVal

hashmap转换treemap hashmap转换成数组_数组_03

2.3 Node

HashMap的Node有两种功能,

(1)直接存储kv;

(2)转换为单向链表;

源码如下图所示,有源码可知,属性key、value即可直接存储kv;

属性next即单向链表结构,使用链表存储数据。

hashmap转换treemap hashmap转换成数组_单向链表_04

2.4 TreeNode

前面的Node用于存储kv或者单向链表,

那么,红黑树则使用TreeNode存储数据,

属性,parent连接父节点,left和right是该节点的左右节点,prev前驱节点,删除时断开与next的连接,

red用于标识当前节点的特性:红或黑。

位置:java.util.HashMap.TreeNode

hashmap转换treemap hashmap转换成数组_java_05

2.5 树化:构建红黑树

树化的方法为treeifyBin,即构建树化容器,

源码如下图所示,

有源码可知,首先构建TreeNode,然后通过treeify构建红黑树,

是的,核心是treeify方法。

位置:java.util.HashMap#treeifyBin

hashmap转换treemap hashmap转换成数组_java_06


构建红黑树:

源码如下图所示。

位置:java.util.HashMap.TreeNode#treeify

hashmap转换treemap hashmap转换成数组_hashmap_07

3 小结

(1)HashMap由3种数据结构构成:数组、单向链表和红黑树;
(2)数据结构转换过程:
(2.1)key没有Hash冲突,则按照kv形式存储,如key2对应的形式;
(2.2)key有Hash冲突且冲突的key个数小于7个,使用链表存储,如key1对应的形式;
(2.3)key有Hash冲突且冲突的key个数大于等于7个,使用红黑树存储,如key3对应的形式。
(3)红黑树转换时,先构建TreeNode数组,然后转换为红黑树,红黑树转换的核心方法为:java.util.HashMap.TreeNode#treeify。