调试JDK源代码-一步一步看HashMap怎么Hash和扩容
调试JDK源代码-一步一步看HashMap怎么Hash和扩容
调试JDK源代码-ConcurrentHashMap实现原理
调试JDK源代码-调试JDK源代码-Hashtable实现原理以及线程安全的原因
还是调试源代码最好。
开发环境 JDK1.8+NetBeans8.1
说明:调试HashMap的 public V put(K key, V value) 方法并查看key的值时不能显示变量的值,原因在于oracle提供的jre中rt.jar不带debug信息。
orcale在编译src时使用了 javac -g:none。意思是不带不论什么调试信息,这样能够减小rt.jar的大小。若想正常调试jdk,就仅仅能又一次编译src.zip。
当然也能够仅仅编译单个须要关注的java就可以,比如HashMap.java。
一.解压src.zip
解压src.zip到E:\workspace\下,
src.zip在安装的C:\Program Files\Java\jdk1.8.0_25下
二.javac -g重编译
又一次编译src\java\util下的HashMap.java
Windows下进入DOS环境。输入
E:\workspace\src\java\util
然后再输入E:就直接到了E:\workspace\src\java\util
默认假设不带-g编译是没有调试信息是不够的。
# javac -g HashMap.java
三.替换rt.jar
将编译好的全部的HashMap.class都放入C:\Program Files\Java\jdk1.8.0_25\jre\lib的rt.jar
说明:须要做好备份以防搞错。
四.调试HashMap
先看看HashMap的理论吧
import java.util.HashMap;
import java.util.Map;
import org.junit.Test; public class TestHash { @Test
public void testHashMap() throws Exception {
System.out.println("==========================");
Map<String, String> m = new HashMap<String, String>();
for (int i = 0; i < 18; i++) {
m.put((char) (i + 65) + (char) (i + 66) + (char) (i + 67) + "", i + ">>>http://blog.csdn.net/unix21/");
}
System.out.println("==========================");
}
}
以下是源代码
/**
* Associates the specified value with the specified key in this map.
* If the map previously contained a mapping for the key, the old
* value is replaced.
*
* @param key key with which the specified value is to be associated
* @param value value to be associated with the specified key
* @return the previous value associated with <tt>key</tt>, or
* <tt>null</tt> if there was no mapping for <tt>key</tt>.
* (A <tt>null</tt> return can also indicate that the map
* previously associated <tt>null</tt> with <tt>key</tt>.)
*/
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
} /**
* Implements Map.put and related methods
*
* @param hash hash for key
* @param key the key
* @param value the value to put
* @param onlyIfAbsent if true, don't change existing value
* @param evict if false, the table is in creation mode.
* @return previous value, or null if none
*/
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
1.第一次进入源代码
先初始化增长因子
一開始声明一个
transient Node<K,V>[] table;
java 的transientkeyword为我们提供了便利,你仅仅须要实现Serilizable接口,将不须要序列化的属性前加入keywordtransient。序列化对象的时候,这个属性就不会序列化到指定的目的地中。
函数体内声明一个Node<K,V>[] tab
一開始table=null。所以tab也是null的
能够看到n=16。假设不使用-g编译是看不到n的,这说明初始的tab长度是16。
然后给tab进行初始化。p=tab[0]=null
2.插入newNode
终于会调用static class Node<K,V>的Node(int hash, K key, V value, Node<K,V> next)
/**
* Basic hash bin node, used for most entries. (See below for
* TreeNode subclass, and in LinkedHashMap for its Entry subclass.)
*/
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next; Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
} public final K getKey() { return key; }
public final V getValue() { return value; }
public final String toString() { return key + "=" + value; } public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
} public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
} public final boolean equals(Object o) {
if (o == this)
return true;
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,? >)o;
if (Objects.equals(key, e.getKey()) &&
Objects.equals(value, e.getValue()))
return true;
}
return false;
}
}
第一个Node节点就有值了。其next为null.
关于静态嵌套类
3.回到putVal
tab[0]就是返回的Node
4.查看是否须要扩容
还不到threshold的上限12 ,所以无需扩容。
5.HashMap第二次put进入putVal
非常显然这个时候table不为空,由于前次已经插值了。
i=3,p=tab[3]
新的node插入在tab[3]上,此次依旧无需扩容。
第4次插值
第7次插值
第11次
第12次
第13次
tab和
此次须要扩容
点开oldTab
下一步
下一步
下一步。threshold升为24
下一步
newTab
oldTab
oldTab[0]
oldTab[j] = null;
下一步
下一步next = e.next;
下一步
下一步
下一步
下一步(e = next) != null
下一步
经过N此循环之后
newTab
oldTab
回到putVal
扩容之后再次进入第14次进入
tab
关于HashMap就分析到此,网上有几篇写的不错的帖子结合看看就更明确了,建议阅读下:
深入Java集合学习系列:HashMap的实现原理 引文 深入Java集合学习系列:HashMap的实现原理 原文
HashMap什么时候进行扩容呢?当HashMap中的元素个数超过数组大小*loadFactor时,就会进行数组扩容。loadFactor的默认值为0.75,这是一个折中的取值。
也就是说。默认情况下。数组大小为16。那么当HashMap中元素个数超过16*0.75=12(这个值就是代码中的threshold值,也叫做临界值)的时候,就把数组的大小扩展为 2*16=32。即扩大一倍。
然后又一次计算每一个元素在数组中的位置,而这是一个很消耗性能的操作,所以假设我们已经预知HashMap中元素的个数,那么预设元素的个数可以有效的提高HashMap的性能。
调试JDK源代码-一步一步看HashMap怎么Hash和扩容的更多相关文章
- 如何调试JDK源代码并查看局部变量值
如下图: 按F5进入String.startsWith,如下: 点“Edit Source Lookup Path” 附加源代码,如下图: 附加上源代码后如下: 可以看到,当鼠标放在“prefix”上 ...
- Eclipse/Myeclipse中查看和调试JDK源代码的方法
看过这篇文章后,实践写的 http://blog.csdn.net/qq_27857857/article/details/71250401 一共做了以下几部: 第一步: 第二步: 一直next,到第 ...
- JDK源代码学习-ArrayList、LinkedList、HashMap
ArrayList.LinkedList.HashMap是Java开发中非常常见的数据类型.它们的区别也非常明显的,在Java中也非常具有代表性.在Java中,常见的数据结构是:数组.链表,其他数据结 ...
- 在ASP.NET 5项目中使用和调试外部源代码包
(此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 题记:由于在ASP.NET 5中,项目依赖都是通过"包"来引用,所以使用 ...
- 从零开始一起学习SLAM | 理解图优化,一步步带你看懂g2o代码
首发于公众号:计算机视觉life 旗下知识星球「从零开始学习SLAM」 这可能是最清晰讲解g2o代码框架的文章 理解图优化,一步步带你看懂g2o框架 小白:师兄师兄,最近我在看SLAM的优化算法,有种 ...
- 转:IDEA中如何使用debug调试项目 一步一步详细教程
原文链接:http://www.yxlzone.top/show_blog_details_by_id?id=2bf6fd4688e44a7eb560f8db233ef5f7 在现在的开发中,我们经常 ...
- IDEA中如何使用debug调试项目 一步一步详细教程
转载该文章:https://blog.csdn.net/yxl_1207/article/details/80973622 一.Debug开篇 首先看下IDEA中Debug模式下的界面. 如下是在ID ...
- (原创)超详细一步一步在eclipse中配置Struts2环境,无基础也能看懂
(原创)超详细一步一步在eclipse中配置Struts2环境,无基础也能看懂 1. 在官网https://struts.apache.org下载Struts2,建议下载2.3系列版本.从图中可以看出 ...
- configure - 源代码安装的第一步
configure是源代码安装的第一步,主要的作用是对即将安装的软件进行配置,检查当前的环境是否满足要安装软件的依赖关系 configure有许多参数可配,具体参见./configure --help ...
随机推荐
- 如何删除sublime目录
左侧栏的sublime目录一直删不掉,删除列直接变成了灰色. 今天才发现应该选择文件夹右击选择工程——从工程中删除文件夹. 这个设计真的很醉,删除这么常用的键还放进了第二层……
- 通过Maven将指定Jar包下载到指定的本地目录
现在大家大部分都通过Maven等工具来管理包,但是特殊情况下还是需要将包下载到本地.我们可以通过maven命令来完成这个需求.创建一个pom.xml文件,文件内容如下: <?xml versio ...
- lsb_release No LSB modules are available
lsb_release 提示: No LSB modules are available 执行: sudo apt-get install lsb-core
- 03Servlet API
Servlet API Servlet是实现javax.servlet.Servlet接口的对象.大多数Servlet通过从GenericServlet或HttpServlet类进行扩展来实现.Ser ...
- 面向对象程序设计--Java语言第二周编程题:有秒计时的数字时钟
有秒计时的数字时钟 题目内容: 这一周的编程题是需要你在课程所给的时钟程序的基础上修改而成.但是我们并不直接给你时钟程序的代码,请根据视频自己输入时钟程序的Display和Clock类的代码,然后来做 ...
- ie7下设置z-index无效如何解决?
ie7下z-index无效的问题之前做练习的时候遇到过,百度解决掉之后就丢脑后了.今天项目中又发现这个bug,无奈又去百度,这次还是记下来,节省了百度的时间还能小装一把... 需求是这样的: 页面中的 ...
- Extjs选中多行Grid提交
要实现的效果如图:可以选择多行grid然后提交给后台 1,Extjs中grid如何可以选择多行? 定义一个grid,将色了Type设置为多选即可 selType: 'checkboxmodel', 2 ...
- HDU-4705 Y(思维+dfs树)
Input 4 1 2 1 3 1 4 Output 1 题意:给你一颗树,选择一个三个点构成的集合,使得这三个点不在一条直线上(意思就是 从一个点出发,用一条不回头的线不能将这三个点连起来)问一共有 ...
- C++字符串处理函数总结
1.基础函数输入输出:cin,cout,getchar,gets,putchar,puts,printf,scanf格式化:sprintf,sprintf_s,wsprintf,wsprintf_s, ...
- Window下的———TOMCAT环境的配置
1. 先去官方网站下载需要的猫(tomcat) http://tomcat.apache.org/ 2.下载好包,然后解压出来,放在你需要的位置上 3.去到配环境变量的地方,进行相应的环境配置 ...