jdk1.8 HashMap红黑树操作详解-putTreeVal()
以前也看过hashMap源码不过是看的jdk1.7的,由于时间问题看的也不是太深入,只是大概的了解了一下他的基本原理;这几天通过假期的时间就对jdk1.8的hashMap深入了解了下,相信大家都是对红黑树和hashMap的扩容机制resize()比较感兴趣,红黑树也是jdk1.8对hashMap新加的一种数据结构,单纯的树形结构挺简单的,不过红黑树是一种自动保持平衡的树形结构,那就比较复杂了,可以通过我另一个博客可以先看一下红黑树的原理再看hashMap中的红黑树就简单多了连接<<<<<,hashMap的扩容机制resize()连接<<<<,废话不多说看代码(一些解释都在代码中)
/**
* 插入树形类型的元素
* Tree version of putVal.
*
* 操作:将插入节点的hash值与每个节点的hash值进行比较,
* 如果是小于那下一次插入节点就与当前节点的左子节点比,反之则与右子节点比,
* 直到当前节点的(左或右)子节点为null,将其插入;
* 每当插入一次节点都会调用一次方法balanceInsertion(root, x)将红黑树进行一个平衡操作
*/
final TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab,
int h, K k, V v) {
Class<?> kc = null;
boolean searched = false;
//获取树的根节点
TreeNode<K,V> root = (parent != null) ? root() : this;
for (TreeNode<K,V> p = root;;) {
int dir, ph; K pk;
//h:插入节点的hash值
//p:遍历到的当前节点
if ((ph = p.hash) > h)
dir = -1;
else if (ph < h)
dir = 1;
else if ((pk = p.key) == k || (k != null && k.equals(pk)))
return p;
else if ((kc == null &&
(kc = comparableClassFor(k)) == null) ||
(dir = compareComparables(kc, k, pk)) == 0) {
if (!searched) {
TreeNode<K,V> q, ch;
searched = true;
if (((ch = p.left) != null &&
(q = ch.find(h, k, kc)) != null) ||
((ch = p.right) != null &&
(q = ch.find(h, k, kc)) != null))
return q;
}
dir = tieBreakOrder(k, pk);
} TreeNode<K,V> xp = p;
if ((p = (dir <= 0) ? p.left : p.right) == null) {
Node<K,V> xpn = xp.next;
TreeNode<K,V> x = map.newTreeNode(h, k, v, xpn);
if (dir <= 0)
xp.left = x;
else
xp.right = x;
xp.next = x;
x.parent = x.prev = xp;
if (xpn != null)
((TreeNode<K,V>)xpn).prev = x;
//balanceInsertion(root, x):此方法是对红黑树进行平衡操作
moveRootToFront(tab, balanceInsertion(root, x));
return null;
}
}
} /**
* 插入元素 平衡红黑树的方法
*
* 注:不管遇到一下三种情况任意一种情况就会进行平衡调整 ,反之不需要;如果x节点是第1种情况,必然会经历2,3;如果x节点是第3种情况不会经历1,2;
*
* 1. 插入节点的父节点和其叔叔节点(祖父节点的另一个子节点)均为红色的;
*
* 2. 插入节点的父节点是红色,叔叔节点是黑色,且插入节点是其父节点的右子节点;
*
* 3. 插入节点的父节点是红色,叔叔节点是黑色,且插入节点是其父节点的左子节点。
*
* @param root
* @param x
* @return
*/
static <K, V> TreeNode<K, V> balanceInsertion(TreeNode<K, V> root, TreeNode<K, V> x) {
//将插入的节点涂成红色
x.red = true;
//此时x节点是刚插入的节点
// 这些变量名不是作者随便定义的都是有意义的。
// xp:x parent,代表x的父节点。
// xpp:x parent parent,代表x的祖父节点
// xppl:x parent parent left,代表x的祖父的左节点。
// xppr:x parent parent right,代表x的祖父的右节点。
for (TreeNode<K, V> xp, xpp, xppl, xppr;;) {
//如果x.parent==null证明x是根节点,并将x(根节点)返回;平衡完毕。
if ((xp = x.parent) == null) {
//将根节点涂成黑色
x.red = false;
return x;
//只要进入此if就不会满足注释中3条件的任意一个,直接将root返回;平衡完毕。
} else if (!xp.red || (xpp = xp.parent) == null)
return root;
//若父节点是祖父节点的左子节点,与下面的完全相反,本质是一样的
if (xp == (xppl = xpp.left)) {
//x节点的祖父节点的右子节点(叔叔节点)不为null且是红色,父节点必然也是红色,此时满足第1种情况。
//操作:1.将祖父节点的右子节点(叔叔节点)、父节点涂为黑色
// 2.将祖父节点涂为红色
// 3.将祖父节点赋给x(参照节点的变更)
if ((xppr = xpp.right) != null && xppr.red) {
xppr.red = false;
xp.red = false;
xpp.red = true;
x = xpp;
//进入else 说明已经是第2或第3种情况了
} else {
//第2种情况
// 操作:1.标记节点变为x。
// 2.左旋
// 3.x的父节点、x的祖父节点随之变化
if (x == xp.right) {
root = rotateLeft(root, x = xp);
xpp = (xp = x.parent) == null ? null : xp.parent;
}
//第3种情况
//操作 1.将父节点涂黑
// 2.祖父节点涂红
// 3.右旋
if (xp != null) {
xp.red = false;
if (xpp != null) {
xpp.red = true;
root = rotateRight(root, xpp);
}
}
}
} else {//若父节点是祖父节点的右子节点,与上面的完全相反,本质一样的
if (xppl != null && xppl.red) {
xppl.red = false;
xp.red = false;
xpp.red = true;
x = xpp;
} else {
if (x == xp.left) {
root = rotateRight(root, x = xp);
xpp = (xp = x.parent) == null ? null : xp.parent;
}
if (xp != null) {
xp.red = false;
if (xpp != null) {
xpp.red = true;
root = rotateLeft(root, xpp);
}
}
}
}
}
}
左旋和右旋只通过代码说,没有图听不明白,必须通过上边说的红黑树原理的那篇博客结合代码才容易看明白,先上一个那篇博客中的左旋和右旋的动态图先了解一下
左旋有个很萌萌哒的动态示意图,可以方便理解:
右旋也有个很萌萌哒的动态示意图,可以方便理解
hashMap的代码我就不贴了大家可以自己看一下
jdk1.8 HashMap红黑树操作详解-putTreeVal()的更多相关文章
- 红黑树原理详解及golang实现
目录 红黑树原理详解及golang实现 二叉查找树 性质 红黑树 性质 operation 红黑树的插入 golang实现 类型定义 leftRotate RightRotate Item Inter ...
- jdk1.8 HashMap的keySet方法详解
我在看HashMap源码的时候有一个问题让我产生了兴趣,那就是HashMap的keySet方法,没有调用HashMap的有关数据的任何方法就能获取到map的所有的键,他是怎么做到的,然后我就通过模拟k ...
- VC++常用数据类型及其操作详解
原文地址:http://blog.csdn.net/ithomer/article/details/5019367 VC++常用数据类型及其操作详解 一.VC常用数据类型列表 二.常用数据类型转化 2 ...
- 史上最全HashMap红黑树解析
HashMap红黑树解析 红黑树介绍 TreeNode结构 树化的过程 红黑树的左旋和右旋 TreeNode的左旋和右旋 红黑树的插入 TreeNode的插入 红黑树的删除 TreeNode的删除节点 ...
- MongoDB各种查询操作详解
这篇文章主要介绍了MongoDB各种查询操作详解,包括比较查询.关联查询.数组查询等,需要的朋友可以参考下 一.find操作 MongoDB中使用find来进行查询,通过指定find的第一个参数可 ...
- Linux Shell数组常用操作详解
Linux Shell数组常用操作详解 1数组定义: declare -a 数组名 数组名=(元素1 元素2 元素3 ) declare -a array array=( ) 数组用小括号括起,数组元 ...
- [Android新手区] SQLite 操作详解--SQL语法
该文章完全摘自转自:北大青鸟[Android新手区] SQLite 操作详解--SQL语法 :http://home.bdqn.cn/thread-49363-1-1.html SQLite库可以解 ...
- shell字符串操作详解
shell字符串操作详解的相关资料. 1.shell变量声明的判断 表达式 含义 ${var} 变量var的值, 与$var相同 ${var-DEFAULT} 如果var没有被声明, 那么就以$DE ...
- memcached 命令操作详解
memcached 命令操作详解 一.存储命令 存储命令的格式: <command name> <key> <flags> <exptime> < ...
随机推荐
- Github.Git
Github介绍:http://www.yangzhiping.com/tech/github.html Git:http://res.crossincode.com/wechat/git.html
- git lg 配置
git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d ...
- How to Enable TLS 1.2 on Windows Server 2008 R2 and IIS 7.5
Nowadays there is an SSL vulnerability called POODLE discovered by Google team in SSLv3 protocol. So ...
- asp.net core使用jexus部署在linux无法正确 获取远程ip的解决办法
asp.net core程序部署在centos7(下面的解决方案,其他系统都能使用,这里只是我自己部署在centos7),使用服务器jexus进行部署,AppHost模式. 因为请求是由jexus进行 ...
- 数组排列组合问题——BACKTRACKING
BACKTRACKING backtracking(回溯法)是一类递归算法,通常用于解决某类问题:要求找出答案空间中符合某种特定要求的答案,比如eight queens puzzle(将国际象棋的八个 ...
- ABPZero中的Name和SurName处理,以及EmailAddress解决方案(完美)。
使用ABPzero的朋友们都知道,User表中有Name和Surname两个字段,这两个字段对于国内的用户来说相当的不友好. 以及我们的一些系统中是不会涉及到EmailAddress字段.也就是说不会 ...
- Rancher + k8s + docker 部署资料
一.k8s 文档: https://jimmysong.io/kubernetes-handbook/concepts/deployment.html 命令行大全 https://kubernetes ...
- Nagios监控系统部署(源码)
1. 概述2. 部署Nagios2.1 创建Nagios用户组2.2 下载Nagios和Nagios-plugin源码2.3 编译安装3. 部署Nagios-plugin3.1 编译安装nagios- ...
- FFMPEG指令
FFmpeg是一个用于音视频处理的自由软件,被广泛用于音视频开发.FFmpeg功能强大,本文主要介绍如何使用FFmpeg命令行工具进行简单的视频处理. 安装FFmpeg可以在官网下载各平台软件包或者静 ...
- 程序员从技术开发到项目管理PM--思维转变
对以往所做项目的经验做下总结,作为项目经理首先要对项目负责,思维要做下转变,要从项目全局角度考虑问题: 从个人成就到团队成就. 无论是做管理还是做技术,成就导向意识是优秀员工的基本素质.只有具 ...