对把JDK源码的一些注解,笔记
//对treeMap的红黑树理解注解. 2017.02.16 by 何锦彬 JDK,1.7.51 /** From CLR */
private void fixAfterInsertion(Entry<K, V> x) { //新加入红黑树的默认节点就是红色
x.color = RED;
/**
* 1. 如为根节点直接跳出
*/
while (x != null && x != root && x.parent.color == RED) { if (parentOf(x) == leftOf(parentOf(parentOf(x)))) { //如果X的父节点(P)是其父节点的父节点(G)的左节点
//即 下面这种情况
/**
* G
* P(RED) U
*/
//获取其叔(U)节点
Entry<K, V> y = rightOf(parentOf(parentOf(x)));
if (colorOf(y) == RED) {
// 这种情况
/**
* G
* P(RED) U(RED)
* X
*/
//如果叔节点是红色的(父节点有判断是红色). 即是双红色,比较好办,通过改变颜色就行. 把P和U都设置成黑色然后,X加到P节点。 G节点当作新加入节点继续迭代
setColor(parentOf(x), BLACK);
setColor(y, BLACK);
setColor(parentOf(parentOf(x)), RED);
x = parentOf(parentOf(x));
} else {
//处理红父,黑叔的情况
if (x == rightOf(parentOf(x))) {
// 这种情况
/**
* G
* P(RED) U(BLACK)
* X
*/
//如果X是右边节点
x = parentOf(x);
// 进行左旋
rotateLeft(x);
}
//左旋后,是这种情况了
/**
* G
* P(RED) U(BLACK)
* X
*/ // 到这,X只能是左节点了,而且P是红色,U是黑色的情况
//把P和G改成黑色,以G为节点进行右旋
setColor(parentOf(x), BLACK);
setColor(parentOf(parentOf(x)), RED);
rotateRight(parentOf(parentOf(x)));
}
} else {
//父节点在右边的
/**
* G
* U P(RED)
*/
//获取U
Entry<K, V> y = leftOf(parentOf(parentOf(x))); if (colorOf(y) == RED) {
//红父红叔的情况
/**
* G
* U(RED) P(RED)
*/
setColor(parentOf(x), BLACK);
setColor(y, BLACK);
setColor(parentOf(parentOf(x)), RED);
//把G当作新插入的节点继续进行迭代
x = parentOf(parentOf(x));
} else {
//红父黑叔,并且是右父的情况
/**
* G
* U(RED) P(RED)
*/
if (x == leftOf(parentOf(x))) {
x = parentOf(x);
//以P为节点进行右旋
rotateRight(x);
}
//右旋后
/**
* G
* U(BLACK) P(RED)
* X
*/
setColor(parentOf(x), BLACK);
setColor(parentOf(parentOf(x)), RED);
//以G为节点进行左旋
rotateLeft(parentOf(parentOf(x)));
}
}
}
//红黑树的根节点始终是黑色
root.color = BLACK;
}
2, HASHMAP的死链问题
//对HashMap死链理解的注解 . 2017.02.17 by 何锦彬 JDK,1.7.51
void transfer(Entry[] newTable, boolean rehash) {
//获取新table的容量
int newCapacity = newTable.length;
//迭代以前的数组
for (Entry<K,V> e : table) {
//如果数组上有元素
while(null != e) {
// 赋值next
Entry<K,V> next = e.next;
//获取e在新的table里的位置
if (rehash) {
e.hash = null == e.key ? 0 : hash(e.key);
}
int i = indexFor(e.hash, newCapacity);
//把e指向当前的新数组里的第一个元素,这里会并发了,如果在这断点等待下个线程过来,就会死循环,尝试下
e.next = newTable[i];
//替代新数组的位置
newTable[i] = e;
e = next;
}
}
}
扩容前
[ 1 ] [ 2 ] [ 3 ] [ 空]
5 10
第一个线程扩容后,数组链表如下
[ 1 ] [ 10 ] [3] [] [] [] []
2
第二个线程又把从头把2指向10,然后2和10形成了个死循环
HashMap在 JDK8后 把数组链表变成了数组+链表+红黑树. 链表为O(n),而红黑树为O(logN)
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
//JDK8 的hashmap,链表到了8就需要变成颗红黑树了
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
调整红黑树的方法其实和treeMap的一样了
如下:
//hashmap的红黑树平衡
static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
TreeNode<K,V> x) {
x.red = true;
//死循环加变量定义,总感觉JAVA很少这样写代码 哈
for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {
//xp X父节点, XPP X的祖父节点, XPPL 祖父左节点 XXPR 祖父右节点
if ((xp = x.parent) == null) {
x.red = false;
return x;
}
// 如果父节点是黑色, 或者XP父节点是空,直接返回
else if (!xp.red || (xpp = xp.parent) == null)
return root; // 下面的代码就和上面的很treeMap像了, if (xp == (xppl = xpp.left)) {
// 第一种情况, 赋值xppl
//父节点是左节点的情况,下面这种
/**
* G
* P(RED) U
*/
if ((xppr = xpp.right) != null && xppr.red) {
//如果红叔的情况
// 这种情况
/**
* G
* P(RED) U(RED)
* X
*/
//改变其颜色,
xppr.red = false;
xp.red = false;
xpp.red = true;
x = xpp;
}
else {
// 黑叔的情况
// 这种情况
/**
* G
* P(RED) U(BLACK)
*/
if (x == xp.right) {
//如果插入节点在右边 这种
// 这种情况
/**
* G
* P(RED) U(BLACK)
* X
*/
//需要进行左旋
root = rotateLeft(root, x = xp);
xpp = (xp = x.parent) == null ? null : xp.parent;
}
//左旋后情况都是这种了
/**
* G
* P(RED) U(BLACK)
* X
*/
// 到这,X只能是左节点了,而且P是红色,U是黑色的情况
if (xp != null) {
//把P和G改成黑色,以G为节点进行右旋
xp.red = false;
if (xpp != null) {
xpp.red = true;
root = rotateRight(root, xpp);
}
}
}
}
else {
//父节点在右边的
/**
* G
* U P(RED)
*/
//获取U
if (xppl != null && xppl.red) {
//红父红叔的情况
/**
* G
* U(RED) P(RED)
*/
xppl.red = false;
xp.red = false;
xpp.red = true;
x = xpp;
}
else { if (x == xp.left) {
//如果插入的X是右节点
/**
* G
* U(BLACK) P(RED)
* X
*/
root = rotateRight(root, x = xp);
xpp = (xp = x.parent) == null ? null : xp.parent;
}
//右旋后
/**
* G
* U(BLACK) P(RED)
* X
*/
if (xp != null) {
xp.red = false;
if (xpp != null) {
xpp.red = true;
root = rotateLeft(root, xpp);
}
}
}
}
}
对把JDK源码的一些注解,笔记的更多相关文章
- Integer.parseInt不同jdk源码解析
执行以下代码: System.out.println(Integer.parseInt("-123")); System.out.println(Integer.parseInt( ...
- 一点一点看JDK源码(一)Collection体系概览
一点一点看JDK源码(一)Collection体系概览 liuyuhang原创,未经允许进制转载 本文举例使用的是JDK8的API 目录:一点一点看JDK源码(〇) 1.综述 Collection为集 ...
- 一点一点看JDK源码(三)java.util.ArrayList 前偏
一点一点看JDK源码(三)java.util.ArrayList liuyuhang原创,未经允许禁止转载 本文举例使用的是JDK8的API 目录:一点一点看JDK源码(〇) 1.综述 ArrayLi ...
- 如何阅读JDK源码
JDK源码阅读笔记: https://github.com/kangjianwei/LearningJDK 如何阅读源码,是每个程序员需要面临的一项挑战. 为什么需要阅读源码?从实用性的角度来看,主要 ...
- 如何有效的阅读JDK源码
阅读Java源码的前提条件: 1.技术基础 在阅读源码之前,我们要有一定程度的技术基础的支持. 假如你从来都没有学过Java,也没有其它编程语言的基础,上来就啃<Core Java>,那样 ...
- 利用IDEA搭建JDK源码阅读环境
利用IDEA搭建JDK源码阅读环境 首先新建一个java基础项目 基础目录 source 源码 test 测试源码和入口 准备JDK源码 下图框起来的路径就是jdk的储存位置 打开jdk目录,找到sr ...
- 重新编译jdk源码,启用debug信息
我有一个不知道是好还是不好的习惯,搞不懂的一些玩意儿,喜欢调试然后单步执行看这玩意儿到底是怎么运行的. 今天看到正则表达式的时候,appendReplacement()这个方法怎么也看不明白它是怎么工 ...
- 使用NetBeans、Eclipse阅读JDK源码
下面说明在Netbeans.Eclipse环境下怎么查看JDK源码: Netbeans: 在"工具->java平台->源"里添加下路径,如果你安装jdk的时候选择安装了 ...
- eclipse下导入jdk源码
一直想好好看看jdk的源码,虽然可以直接解压jdk下的src看,但是终究不方便!后来发现可以导入到eclipse中,就在网上找了一些方法,下面就和大家分共享: step1:打开eclipse选择Win ...
随机推荐
- Codeforces Round #411 (Div. 2)(A,B,C,D 四水题)
A. Fake NP time limit per test:1 second memory limit per test:256 megabytes input:standard input out ...
- [51nod1410]回文调整
给一个序列,选择其中一个区间,这个区间内的数字顺序可以随意互换.问有多少这样的选择使得整个序列(不是选择的区间)是一个回文. 说明:为了要使得整个序列是一个回文,可以选择一个区间对里面的数字进行调整, ...
- Ping pong(树状数组求序列中比某个位置上的数小的数字个数)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2492 Ping pong Time Limit: 2000/1000 MS (Java/Others) ...
- 【笔记】移动端H5数字键盘input type=number的处理(IOS和Android)
在Vue中的项目,基于VUX-UI开发,一个常见的需求: 1.金额输入框 2.弹出数字键盘 3.仅支持输入两位小数,限制最大11位数,不允许0开头 后续:与UI沟通后, 思路调整为限制输入,并减少正则 ...
- [国嵌攻略][100][嵌入式Linux内核制作]
Linux内核制作步骤 1.清除原有配置 make distclean 2.配置内核 选择一个已有的配置文件简化配置 make menuconfig ARCH=arm 3.编译内核 ARCH指明处理器 ...
- javascript之fill()方法
无意中看到fill这个方法,有些不解,起初以为是人家自定义的方法,后来才发觉原来不是,javascript里面是真的有这个方法,于是特地学习了下. fill()方法的作用是使用一个固定值来替换数组中的 ...
- DT_修改注册项
所用语法: /* */用于注释掉某些非必要的判断语句 修改的文件及路径如下: 前台: 文件路径:/根目录/lang/zh-cn/ 编辑文件:member.inc.php 修改其中一些提示信息(如,将[ ...
- linux libpcap的性能问题,请大家注意绕行。
内核代码中,ip_rcv是ip层收包的主入口函数,该函数由软中断调用.存放数据包的sk_buff结构包含有目的地ip和端口信息,此时ip层进行检查,如果目的地ip不是本机,且没有开启转发的话,则将包丢 ...
- Assembly oth
body, table{font-family: 微软雅黑; font-size: 13.5pt} table{border-collapse: collapse; border: solid gra ...
- python正则表达式re模块详细介绍
转自:http://www.jb51.net/article/50511.htm 本模块提供了和Perl里的正则表达式类似的功能,不关是正则表达式本身还是被搜索的字符串,都可以是Unicode字符,这 ...