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 ...
随机推荐
- Atitti css3 新特性attilax总结
Atitti css3 新特性attilax总结 图片发光效果2 透明渐变效果2 文字描边2 背景拉伸2 CSS3 选择器(Selector)4 @Font-face 特性7 Word-wrap &a ...
- VS2012 easyui datagrid url访问之坑
VS2012 easyui datagrid url访问之坑 url属性放的是地址的话 返回的json格式必须有 total 和 rows,如下: {"total":2," ...
- FIR.im Weekly - 这是多产的一周
前方干货颇多,请备好小板凳,泡好茶,慢慢看. 上周四到周六(23号-25号),由 infoQ 主办的 QCon 在北京召开,各技术大牛围绕敏捷开发.技术架构.开发实践等话题进行了技术分享与探讨. QC ...
- Android Studio 使用技巧
1.导入Android Studio 工程的一些技巧 因为Gradle版本的问题,我们在使用AS导入工程的时候,经常会碰到本地没有该项目的Gradle版本,这时候AS就会去下载这个版本的Gradle, ...
- java基础—继承题目:编写一个Animal类,具有属性:种类;具有功能:吃、睡。定义其子类Fish
编写一个Animal类,具有属性:种类:具有功能:吃.睡.定义其子类Fish package zhongqiuzuoye; public class Animal { //属性 private Str ...
- Ztree行政地区树状展示
Ztree行政地区树状展示(点击加载) 效果如下: 开始贴代码: 实体类 Item,用于对Ztree的节点展示 public class Item { private String id; priva ...
- Nexus3.0私服搭建
官方文档:http://books.sonatype.com/nexus-book/3.0/reference/install.html 1.下载 http://www.sonatype.com/do ...
- 行集函数:OpenRowSet 和 OpenQuery
在SQL Server中,行集函数是不确定性的,这意味着,每次调用,返回值不总是相同的.返回值是不确定的,这意味着,对于相同的输入值,不保证每次返回的值都是相同的.对行集函数的每次调用,行集函数都是单 ...
- 用CSS text-transform转换字母大小写
以前我们都是用JS来实现字母的首字母大小写的,但现在没有必要了,CSS完全可以实现,话说兼容性还好从IE6开始就支持了. text-transform:none | capitalize | uppe ...
- Linux下程序包管理工具RPM
实验环境: CentOS release 6.6 (Final) 一台 IP地址:172.16.249.230 RPM 是 Red Hat Package Manager 的缩写,本意是Red Ha ...