Java --HashMap源码解析
兴趣所致研究一下HashMap的源码,写下自己的理解,基于JDK1.8。
本文大概分析HashMap的put(),get(),resize()三个方法。
首先让我们来看看put()方法。
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.首先通过hash(key)计算key的hash值
putVal(hash(key), key, value, false, true)
2.由于hashMap实际存储数据的就是table数组,那么首先需要判断table数组是否已经被初始化了,如果没有初始化,那么调用resize()方法对table进行初始化
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
3.通过hash与(n-1)进行与运算得出数组的索引,根table[索引]判断是否为null,如果为null那么直接存入该位置。
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
4.如果table[索引]不为null,那么说明位置发生了碰撞,需要继续进行判断
5.如果判断table[索引]位置p上的p.key=key&&p.hash=hash,那么就会对value进行替换,也就是保证key唯一。
6.如果不满足5的条件,那么会判断table[索引]位置p如果是二叉树节点,那么会调用TreeNode.putTreeVal()去进行对二叉树节点的插入。
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
7.如果不满足5,6的条件,那么会使用链表来插入该节点:循环寻找p.next直到找到一个位置为null那么进行插入。
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;
}
8.上述5,6,7三步中,只要发现了p.key=key&&p.hash=hash,那么就会进行value替换,将oldValue替换为newValue。
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
9.进行计数+1,并且判断当前已存数量是否大于table[]的2/3,如果大于那么使用resize()对table进行扩充。
10.至此整个put()完成。
再来让我们看看get()方法。
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
/**
* Implements Map.get and related methods
*
* @param hash hash for key
* @param key the key
* @return the node, or null if none
*/
final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
if ((e = first.next) != null) {
if (first instanceof TreeNode)
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
return null;
}
1.首先判断table[]是否为空,通过hash计算索引判断table[索引]是否为空,如果任意一项为空那么直接return null。
2.判断table[索引]的hash与key是否都与查找的相同,如果hash与key都相同,那么直接返回table[索引]。
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
3.如果不满足条件2,那么判断table[索引]的next是否为空,如果为空则return null。
4.如果table[索引]的next不为空,那么判断是否为二叉树,如果是二叉树直接使用getTreeNode()方法查找。
if (first instanceof TreeNode)
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
5.如果不是二叉树,那么直接使用链表的方式查找。
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
6.至此完成整个get()方法。
Java --HashMap源码解析的更多相关文章
- 【转】Java HashMap 源码解析(好文章)
.fluid-width-video-wrapper { width: 100%; position: relative; padding: 0; } .fluid-width-video-wra ...
- Java HashMap 源码解析
今天正式开始分析具体集合类的代码,首先以既熟悉又陌生的HashMap开始. 签名(signature) public class HashMap<K,V> extends Abstract ...
- Java——HashMap源码解析
以下针对JDK 1.8版本中的HashMap进行分析. 概述 哈希表基于Map接口的实现.此实现提供了所有可选的映射操作,并且允许键为null,值也为null.HashMap 除了不支持同步操 ...
- Java——LinkedHashMap源码解析
以下针对JDK 1.8版本中的LinkedHashMap进行分析. 对于HashMap的源码解析,可阅读Java--HashMap源码解析 概述 哈希表和链表基于Map接口的实现,其具有可预测的迭 ...
- Java中的容器(集合)之HashMap源码解析
1.HashMap源码解析(JDK8) 基础原理: 对比上一篇<Java中的容器(集合)之ArrayList源码解析>而言,本篇只解析HashMap常用的核心方法的源码. HashMap是 ...
- HashMap源码解析 非原创
Stack过时的类,使用Deque重新实现. HashCode和equals的关系 HashCode为hash码,用于散列数组中的存储时HashMap进行散列映射. equals方法适用于比较两个对象 ...
- Java HashMap源码详解
Java数据结构-HashMap 目录 Java数据结构-HashMap 1. HashMap 1.1 HashMap介绍 1.1.1 HashMap介绍 1.1.2 HashMap继承图 1.2 H ...
- 自学Java HashMap源码
自学Java HashMap源码 参考:http://zhangshixi.iteye.com/blog/672697 HashMap概述 HashMap是基于哈希表的Map接口的非同步实现.此实现提 ...
- Java集合类源码解析:Vector
[学习笔记]转载 Java集合类源码解析:Vector 引言 之前的文章我们学习了一个集合类 ArrayList,今天讲它的一个兄弟 Vector.为什么说是它兄弟呢?因为从容器的构造来说,Vec ...
随机推荐
- WPF仿Win7便笺
最近用WPF写了个仿WIN7下面的便笺小工具,还算是比较华丽的,相似度99%以上吧,在集成了便笺原有的功能以外,当然也做了略微的修改,比如加了关于作者版本信息和修改了原有删除便笺的系统弹出框.软件开机 ...
- iOS-----About Asset Catalog
About Asset Catalogs Use asset catalogs to simplify management of images that are used by your app a ...
- Jquery实现AJAX拦截
前几天项目需要实现一个AJAX拦截,于是就用jquery写了一个,这里分享一下. 需求是这样的,ajax不是我来写,所有说我是不能动ajax的,并且我也不知道什么时候它会发生,为了方便项目经理让我把它 ...
- knockoutjs中使用mapping插件绑定数据列表
使用KO绑定数据列表示例: 1.先申请V,T,T2三个辅助方法,方便调试.声明viewModel和加载数据时的映射条件mapping 2.先使用ko.mapping.fromJS()将原来的 ...
- List和Dictionary泛型类查找效率浅析
List和Dictionary泛型类查找效率存在巨大差异,前段时间亲历了一次.事情的背景是开发一个匹配程序,将书籍(BookID)推荐给网友(UserID),生成今日推荐数据时,有条规则是同一书籍七日 ...
- nodejs Error: request entity too large解决方案
错误如图: 解决方案: app.js添加 var bodyParser = require('body-parser'); app.use(bodyParser.json({limit: '50mb' ...
- Dijkstra算法(二)之 C++详解
本章是迪杰斯特拉算法的C++实现. 目录 1. 迪杰斯特拉算法介绍 2. 迪杰斯特拉算法图解 3. 迪杰斯特拉算法的代码说明 4. 迪杰斯特拉算法的源码 转载请注明出处:http://www.cnbl ...
- 【环境配置】Linux环境下下载、配置java环境、安装eclipse、建立eclipse快捷方式详解
一.首先是下载Java JDK 到目前为止的最新版本为(jdk1.8.0_60),有两种方式进行下载: 1.使用shell来进行下载,可使用如下命令直接进行下载: wget --no-check-ce ...
- android布局文件中android:icon="?attr/menuIconCamera"找不到对应图标路径
如 <item android:id="@+id/camera" android:title="Camera" android:icon="?a ...
- plsql导入excel时报错:ORA-01036: 非法变量名/编号
导入oracle数据,选择工具->odbc导入->Excel 然后关于日期的插入出错,修改后如下: