跳转到主内容
websoft网络软件专家 - 深耕网络技术,打造实用软件!

详解HashMap的TreeNode类结构_红黑树节点与双向链表节点的复合

TreeNode既是红黑树节点又是链表节点,因其继承LinkedHashMap.Entry(含before/after)且源自Node(含prev/next),分别支持树结构(left/right/parent/red)和哈希桶内链表连接(prev/next)。 TreeNode为什么既是红黑树节点又是链表节点 因为
TreeNode
继承自
LinkedHashMap.Entry
,而后者本身就有
before
和
after
字段——这是双向链表的前驱/后继指针。所以它天然支持两种结构:作为红黑树节点时用
left
/
right
/
parent
/
red
;作为插入顺序链表节点时用
prev
/
next
(注意:不是
before
/
after
,
prev
/
next
来自
Node
的继承,用于哈希桶内链表连接)。 常见错误现象:
NullPointerException
出现在遍历
TreeNode
的
prev
或
next
时,其实是误把树节点当普通链表节点直接遍历,忽略了它只在「树化后仍需维持原桶内顺序」时才有效连接。
prev
和
next
仅在该
TreeNode
被挂载到哈希桶数组中时被维护(比如调用
moveRootToFront
后),不是树结构的一部分 树内遍历必须走
left
/
right
,不能靠
next
遍历整棵树 反树化(
untreeify
)时会用
prev
/
next
快速重建链表,但此时
left
/
right
已失效 treeify() 怎么把链表转成红黑树 核心是两步:先构造一棵普通的二叉搜索树(BST),再通过
balanceInsertion
逐个染色+旋转,恢复红黑性质。整个过程不改变原有
Node
的
hash
/
key
/
value
,只是把它们包装成
TreeNode
并重连指针。 使用场景:当某个桶中
Node
链表长度 ≥ 8 且哈希表容量 ≥ 64 时触发,否则只扩容不树化。 树化前会尝试用
comparableClassFor
+
compareComparables
获取 key 的自然序;失败则 fallback 到
tieBreakOrder
(基于
System.identityHashCode
和类名哈希)保证可比性 所有新创建的
TreeNode
默认为红色,插入后统一由
balanceInsertion
调整 注意:树化不是“复制数据”,而是原地升级节点类型——原来的
Node
对象被强转为
TreeNode
,其
next
字段仍保留,用于维持桶内链表结构 removeTreeNode() 删除时最常踩的三个坑 删除操作远比插入复杂,因为要同时维护红黑树平衡和哈希桶链表结构,稍有不慎就断链或失衡。 X-Node企业快速建站1.0.6.0801 特色介绍: 1、ASP+XML+XSLT开发,代码、界面、样式全分离,可快速开发 2、支持语言包,支持多模板,ASP文件中无任何HTML or 中文 3、无限级分类,无限级菜单,自由排序 4、自定义版头(用于不规则页面) 5、自动查找无用的上传文件与空目录,并有回收站,可删除、还原、永久删除 6、增强的Cache管理,可单独管理单个Cache 7、以内存和XML做为Cache,兼顾性能与消耗 8、 下载 常见错误现象:删除后
get()
返回 null、
size()
不对、甚至
ConcurrentModificationException
(多线程下未同步)。 没调用
unmap
或漏掉
root
重连逻辑,导致该桶的
tab[index]
指向一个已从树中摘除但未从桶链表中移出的节点 删除后未调用
balanceDeletion
,红黑树黑高失衡,后续查找可能漏节点(尤其在左/右子树黑高不等时) 反树化阈值判断写错:JDK 规定树中节点 ≤ 6 才
untreeify
,但有人误用“桶中总节点数 ≤ 6”,忽略当前已是树结构的事实 TreeNode 的 color 字段为什么是布尔型而不是枚举 因为
red
字段定义为
boolean red
,不是
Color.RED/BLACK
。这是纯粹的性能取舍:避免对象创建和虚方法分派,用单 bit 存储更省内存、更快访问。 性能影响:在高频插入/删除路径(如
putVal
、
removeNode
)中,每次颜色判断都是
if (p.red)
这种直接位读取,比
if (p.color == Color.RED)
少一次字段解引用和常量比较。 所有红黑树操作(
rotateLeft
、
rotateRight
、
balanceInsertion
)都基于这个布尔值做分支,没有抽象层 别试图给
TreeNode
加 color getter/setter——它压根没提供,直接读写
red
字段是唯一正解 调试时打印
TreeNode
看不到 color?那是
toString()
没输出它,得手动打
p.red ? "RED" : "BLACK"
真正麻烦的从来不是“怎么写 TreeNode”,而是搞清它在哪一刻属于树、在哪一刻属于链表、又在哪一刻两者都得保——这三个身份切换全靠几行指针赋值,手抖一下,
prev
指向了
right
,或者
root
没挪到桶头,问题就藏进去了。

相关文章