讲师中心 微信公众号
AI工具推荐 视频效率加速

详解Java集合中的红黑树旋转逻辑_HashMap树化时的左旋与右旋

雨丽酱_1131

雨丽酱_1131

发布时间:2026-02-22 19:47:02

|

474人浏览过

|

来源于php中文网

原创

HashMap中红黑树旋转仅在树化后插入/删除违反性质时触发,由balanceInsertion/balanceDeletion自动调用包私有rotateLeft/rotateRight,不暴露给用户,且需严格维护parent、next等指针一致性。

详解java集合中的红黑树旋转逻辑_hashmap树化时的左旋与右旋

红黑树左旋右旋在HashMap里到底什么时候触发

不是所有插入都会触发旋转,HashMap只在树化后、且节点数 ≥ TREEIFY_THRESHOLD(默认8)并满足容量 ≥ MIN_TREEIFY_CAPACITY(默认64)时才构造红黑树;旋转只发生在插入或删除导致违反红黑树性质时,比如某节点的右子节点为红色,而该节点本身是黑色且无左子节点——这时必须右旋来重平衡。

常见错误现象:put()后发现树结构异常、get()返回null但键明明存在、调试时看到TreeNode的left/right指针乱指。本质不是旋转写错了,而是没意识到:JDK 8+ 的HashMap中旋转逻辑被封装在balanceInsertion()和balanceDeletion()里,不暴露给用户,你没法手动调用rotateLeft()或rotateRight()。

  • 真正能干预的只有hashCode()实现——若大量键返回相同哈希值,会加速树化,也更容易暴露旋转逻辑缺陷
  • TreeMap才公开了旋转细节(如fixAfterInsertion()),但HashMap的树节点是内部类TreeNode,旋转完全由其静态辅助方法控制
  • 不要试图通过反射修改TreeNode的red字段来“验证”旋转——这会破坏状态一致性,后续操作可能直接抛ConcurrentModificationException

看懂HashMap源码里的rotateLeft和rotateRight实现

JDK 源码中这两个方法定义在HashMap.TreeNode内部类里,是包私有(package-private)的静态方法,签名长这样:static <k> TreeNode<k> rotateLeft(TreeNode<k> root, TreeNode<k> p)</k></k></k></k>。参数p是待旋转的子节点,root是当前子树根,返回新根。

关键点在于:它们不修改parent指针以外的任何引用关系——比如rotateLeft(p)会把p.right提为新父,p变成其左孩子,但p.right.left原指向的节点会被挂到p.right左边,这个“断链再接”的过程极易漏掉parent更新。

立即学习“Java免费学习笔记(深入)”;

java-api-doc-generator
java-api-doc-generator

分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。

下载
  • 典型坑:p.right.left != null时,必须显式设置p.right.left.parent = p,否则该节点脱离树结构
  • root参数实际只用于定位整个子树的入口,旋转后若p原是root,则返回p.right;否则返回传入的root
  • 性能影响:单次旋转是O(1),但频繁树化+旋转说明哈希分布极差,此时应优先检查hashCode()而非优化旋转代码

为什么你的自定义红黑树测试总和HashMap行为对不上

因为HashMap的树节点TreeNode继承自Node,复用了next字段做链表后继,同时又用left/right维护树结构——这是个“链-树二叉混合结构”。标准红黑树教材里的纯树模型,不处理next和prev指针,自然对不上。

更隐蔽的问题是着色规则:教材说“根必须黑”,但HashMap在插入过程中允许临时红根,靠balanceInsertion()最后统一染黑;教材要求“红节点的孩子必为黑”,而HashMap在插入中途会短暂违反,再靠旋转+变色修复。

  • 调试建议:打日志时别只看left/right,务必检查next是否还连着原链表节点,否则split()扩容时会丢数据
  • 兼容性注意:JDK 19+ 对树化阈值做了微调,treeifyBin()现在会先检查表长度,再决定是扩容还是树化,老版本逻辑不能直接套用
  • 别拿TreeMap的旋转步骤硬套——它没有next字段,也不参与哈希桶链表,旋转后无需同步更新链式结构

想改旋转逻辑?先看清楚HashMap的不变量约束

你能安全动的地方极少:balanceInsertion()末尾的root.color = BLACK不能删,rotateLeft()里对p.right.left.parent的赋值不能跳过,否则下一次find()遍历时会因parent == null进入无限循环。

真正容易被忽略的是“双黑”处理:删除时若被删节点是黑且无红孩子,会产生“双黑”缺陷,必须靠balanceDeletion()向上推黑——这个过程可能触发多次旋转,但每次旋转前都先尝试变色,仅当兄弟节点也黑且侄子全黑时才转。

  • 最常漏的检查:if (r == null || r.red)——这里的r是兄弟节点,如果误写成r != null && !r.red,逻辑就反了
  • 参数命名陷阱:源码里xp表示x.parent,xpl是xp.left,看着像缩写,实则是避免重复计算,不是随意简写
  • 别碰moveRootToFront()——它负责把旋转后的新根挪到桶首,和链表头保持一致,改错会导致整个桶不可见
事情说清了就结束

热门AI工具

更多
小橡皮AI

一款AI工具,主要用于小橡皮一键去AI味,将机器味重的内容改得更有活人感,防止因为 AI 内容被平台打上 AI 标签,以及限流,适合需要提升相关任务效率的用户。

二狗PPT
二狗PPT Hot

一款AI演示文稿工具,主要用于专为中式职场打造的AI PPT生成工具,适合需要提升相关任务效率的用户。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

PixPix
PixPix Hot

PixPix是一款面向电商视觉生产的AI商品图生成工具。

超级简历WonderCV

一款AI办公效率工具,主要用于免费求职简历模版下载制作,应届生职场人必备简历制作神器,适合需要提升相关任务效率的用户。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

VibeKnow
VibeKnow Hot

一款AI视频创作工具,主要用于全球首个AI知识视频创作平台,文档、文章、网页,一键生成视频,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

PixTV
PixTV Hot

PixTV是一款面向AIGC内容创作的AI视频生成工具。

相关专题

更多
c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

549

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

1698

2024.03.01

C++ 智能指针与现代内存管理
C++ 智能指针与现代内存管理

深入讲解 C++ 现代内存管理的核心工具——智能指针,涵盖 unique_ptr 独占所有权语义、shared_ptr 引用计数机制与循环引用问题、weak_ptr 弱引用的应用场景、make_unique/make_shared 工厂函数的性能优势、自定义删除器的编写、RAII 资源管理思想的实践,以及从裸指针迁移到智能指针的重构策略,帮助开发者编写安全无泄漏的现代 C++ 代码。

379

2026.04.23

Vite 项目如何打包和部署
Vite 项目如何打包和部署

Vite项目打包部署专题汇集生产构建、产物预览、输出目录、资源路径、Nginx与GitHub Pages部署及常见白屏和路由404问题的已审核文章,帮助读者完成上线流程。

100

2026.10.28

Vite 如何搭配 Vue.js
Vite 如何搭配 Vue.js

Vite搭配Vue.js专题汇集创建Vue 3项目、配置开发环境、路径别名、Vue插件、代理、多环境构建与调试的已审核文章,帮助读者完成从初始化到日常开发的配置。

60

2026.10.28

Vite 项目目录怎么看
Vite 项目目录怎么看

Vite项目目录专题汇集入口文件、src源码、public静态资源、路径别名、项目根目录和构建输出目录相关的已审核文章,帮助读者理解各文件夹的用途与引用方式。

220

2026.10.28

Gradle插件开发入门教程
Gradle插件开发入门教程

本专题介绍Gradle插件开发的基本流程,涵盖插件类编写、自定义任务注册、扩展参数设计和插件应用方法,帮助你理解插件如何组织构建逻辑,减少多个项目之间的重复配置。

140

2026.10.22

Gradle Wrapper配置教程
Gradle Wrapper配置教程

本专题介绍Gradle Wrapper的基本作用,涵盖文件组成、生成方法、版本配置和任务调用,帮助你理解项目如何使用指定的Gradle版本,减少不同开发环境带来的构建差异。

120

2026.10.22

Golangci-lint检查器使用教程
Golangci-lint检查器使用教程

本专题整理Golangci-lint常用Linters的用途与配置方法,覆盖错误处理、无效代码、安全、复杂度、命名和依赖检查,帮助项目合理选规则。

80

2026.10.22

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
dev.java 官方:Learn Java
dev.java 官方:Learn Java

共0课时 | 0人学习

Java JDBC数据库连接官方教程
Java JDBC数据库连接官方教程

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn