以前也看过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()的更多相关文章

  1. 红黑树原理详解及golang实现

    目录 红黑树原理详解及golang实现 二叉查找树 性质 红黑树 性质 operation 红黑树的插入 golang实现 类型定义 leftRotate RightRotate Item Inter ...

  2. jdk1.8 HashMap的keySet方法详解

    我在看HashMap源码的时候有一个问题让我产生了兴趣,那就是HashMap的keySet方法,没有调用HashMap的有关数据的任何方法就能获取到map的所有的键,他是怎么做到的,然后我就通过模拟k ...

  3. VC++常用数据类型及其操作详解

    原文地址:http://blog.csdn.net/ithomer/article/details/5019367 VC++常用数据类型及其操作详解 一.VC常用数据类型列表 二.常用数据类型转化 2 ...

  4. 史上最全HashMap红黑树解析

    HashMap红黑树解析 红黑树介绍 TreeNode结构 树化的过程 红黑树的左旋和右旋 TreeNode的左旋和右旋 红黑树的插入 TreeNode的插入 红黑树的删除 TreeNode的删除节点 ...

  5. MongoDB各种查询操作详解

    这篇文章主要介绍了MongoDB各种查询操作详解,包括比较查询.关联查询.数组查询等,需要的朋友可以参考下   一.find操作 MongoDB中使用find来进行查询,通过指定find的第一个参数可 ...

  6. Linux Shell数组常用操作详解

    Linux Shell数组常用操作详解 1数组定义: declare -a 数组名 数组名=(元素1 元素2 元素3 ) declare -a array array=( ) 数组用小括号括起,数组元 ...

  7. [Android新手区] SQLite 操作详解--SQL语法

    该文章完全摘自转自:北大青鸟[Android新手区] SQLite 操作详解--SQL语法  :http://home.bdqn.cn/thread-49363-1-1.html SQLite库可以解 ...

  8. shell字符串操作详解

    shell字符串操作详解的相关资料. 1.shell变量声明的判断  表达式 含义 ${var} 变量var的值, 与$var相同 ${var-DEFAULT} 如果var没有被声明, 那么就以$DE ...

  9. memcached 命令操作详解

    memcached 命令操作详解 一.存储命令 存储命令的格式: <command name> <key> <flags> <exptime> < ...

随机推荐

  1. Github.Git

    Github介绍:http://www.yangzhiping.com/tech/github.html Git:http://res.crossincode.com/wechat/git.html

  2. git lg 配置

    git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d ...

  3. 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 ...

  4. asp.net core使用jexus部署在linux无法正确 获取远程ip的解决办法

    asp.net core程序部署在centos7(下面的解决方案,其他系统都能使用,这里只是我自己部署在centos7),使用服务器jexus进行部署,AppHost模式. 因为请求是由jexus进行 ...

  5. 数组排列组合问题——BACKTRACKING

    BACKTRACKING backtracking(回溯法)是一类递归算法,通常用于解决某类问题:要求找出答案空间中符合某种特定要求的答案,比如eight queens puzzle(将国际象棋的八个 ...

  6. ABPZero中的Name和SurName处理,以及EmailAddress解决方案(完美)。

    使用ABPzero的朋友们都知道,User表中有Name和Surname两个字段,这两个字段对于国内的用户来说相当的不友好. 以及我们的一些系统中是不会涉及到EmailAddress字段.也就是说不会 ...

  7. Rancher + k8s + docker 部署资料

    一.k8s 文档: https://jimmysong.io/kubernetes-handbook/concepts/deployment.html 命令行大全 https://kubernetes ...

  8. Nagios监控系统部署(源码)

    1. 概述2. 部署Nagios2.1 创建Nagios用户组2.2 下载Nagios和Nagios-plugin源码2.3 编译安装3. 部署Nagios-plugin3.1 编译安装nagios- ...

  9. FFMPEG指令

    FFmpeg是一个用于音视频处理的自由软件,被广泛用于音视频开发.FFmpeg功能强大,本文主要介绍如何使用FFmpeg命令行工具进行简单的视频处理. 安装FFmpeg可以在官网下载各平台软件包或者静 ...

  10. 程序员从技术开发到项目管理PM--思维转变

    对以往所做项目的经验做下总结,作为项目经理首先要对项目负责,思维要做下转变,要从项目全局角度考虑问题:     从个人成就到团队成就. 无论是做管理还是做技术,成就导向意识是优秀员工的基本素质.只有具 ...