调试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 ...
随机推荐
- MyElipse如何添加Emmet插件
把这个jar文件放到myeclipse2014安装目录下dropins文件夹中,然后重启myeclipse即可. 可到window-->perferences里查看,如果成功则会看到emmet选 ...
- NodeJs学习记录(三)vscode下启动一个nodejs的web工程
2017/01/23 星期一 前言:根据手上现成的一个web工程来学习 1.配置vscode使其可以识别nodejs的页面文件.ejs 2.先把项目拖拽至vscode的编辑界面,在"查看&q ...
- [ Luogu 1273 ] 有线电视网
\(\\\) \(Description\) 一棵\(N\)个节点的树,编号在\([N-M+1,N]\)内的点必定为叶子节点,且这些点都有一个收益值\(Val_i\),同时每一条树边都有一个代价. 访 ...
- 自学php【一】 任务:图片上传即时可见
工作已经快2周了,头儿给派了个任务做个企业站!这几天正在紧锣密鼓的作战中!等忙完了这个活!写下自己的学习心得体会!与看到文章的您一起分享! 在这里记录每次遇到的难题,如何解决的! 今天要做的功能就是实 ...
- C++ 模板template和template
原文链接:https://blog.csdn.net/skyleung/article/details/42195509 template<class T>和template<typ ...
- HDU_1143_tri tiling
Tri Tiling Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total ...
- Android(java)学习笔记206:JNI之工具快速开发步骤
下面通过一个案例说明一下,利用工具jni快速开发步骤 1.新建一个Android工程,命名为"03_对int数组加1",如下: 2. 在MainActivity.java中对add ...
- MySql学习笔记(四) —— 数据的分组
前面介绍的聚集函数只是用来计算行数,平均数,最大值,最小值而不用检索所有数据.通过count()函数,我们可以计算生产商1003提供的产品数目,但如果我要查询所有生产商提供的商品数,这就需要进行分组查 ...
- canvas练手项目(三)——Canvas中的Text文本
Canvas中的Text文本也是一个知识点~,我们需要掌握一下几个基本的Text操作方法 首先是重要参数textAlign和textBaseline: textAlign left center ri ...
- JavaScript--小白入门篇2
一.布尔值和关系运算符.逻辑运算符 1.1 布尔值 我们上篇文章说了,学习了两种变量的类型数值型.字符串型. 实际上,还有很多变量的类型.我们今天再学习一种,叫做“布尔类型”. 数值型里面的值 ...