兴趣所致研究一下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源码解析的更多相关文章

  1. 【转】Java HashMap 源码解析(好文章)

    ­ .fluid-width-video-wrapper { width: 100%; position: relative; padding: 0; } .fluid-width-video-wra ...

  2. Java HashMap 源码解析

    今天正式开始分析具体集合类的代码,首先以既熟悉又陌生的HashMap开始. 签名(signature) public class HashMap<K,V> extends Abstract ...

  3. Java——HashMap源码解析

    以下针对JDK 1.8版本中的HashMap进行分析. 概述     哈希表基于Map接口的实现.此实现提供了所有可选的映射操作,并且允许键为null,值也为null.HashMap 除了不支持同步操 ...

  4. Java——LinkedHashMap源码解析

    以下针对JDK 1.8版本中的LinkedHashMap进行分析. 对于HashMap的源码解析,可阅读Java--HashMap源码解析 概述   哈希表和链表基于Map接口的实现,其具有可预测的迭 ...

  5. Java中的容器(集合)之HashMap源码解析

    1.HashMap源码解析(JDK8) 基础原理: 对比上一篇<Java中的容器(集合)之ArrayList源码解析>而言,本篇只解析HashMap常用的核心方法的源码. HashMap是 ...

  6. HashMap源码解析 非原创

    Stack过时的类,使用Deque重新实现. HashCode和equals的关系 HashCode为hash码,用于散列数组中的存储时HashMap进行散列映射. equals方法适用于比较两个对象 ...

  7. Java HashMap源码详解

    Java数据结构-HashMap 目录 Java数据结构-HashMap 1. HashMap 1.1 HashMap介绍 1.1.1 HashMap介绍 1.1.2 HashMap继承图 1.2 H ...

  8. 自学Java HashMap源码

    自学Java HashMap源码 参考:http://zhangshixi.iteye.com/blog/672697 HashMap概述 HashMap是基于哈希表的Map接口的非同步实现.此实现提 ...

  9. Java集合类源码解析:Vector

    [学习笔记]转载 Java集合类源码解析:Vector   引言 之前的文章我们学习了一个集合类 ArrayList,今天讲它的一个兄弟 Vector.为什么说是它兄弟呢?因为从容器的构造来说,Vec ...

随机推荐

  1. fir.im Weekly - 揭秘直播移动 APP 技术实现

    2016年直播似乎无处不在,作为一个开发者也许需要补充下关于直播技术点.本期 fir.im Weekly 整理了一些开发者对于直播实践项目中的技术经验与直播技术架构分析等内容,还有一些关于 iOS . ...

  2. 用Chrome插件对自动化测试TestWriter进行录制

    1.打开Chrome浏览器,在浏览地址中输入: chrome://extensions/,并勾选开发者模式.如图: 2.点击按钮[加载已解压的扩展程序-].如图: 3.选择Testwriter客户端下 ...

  3. js设置自动刷新

    如何实现刷新当前页面呢?借助js你将无所不能. 1,reload 方法,该方法强迫浏览器刷新当前页面.语法:location.reload([bForceGet])   参数: bForceGet, ...

  4. 【WP 8.1开发】推送通知测试服务端程序

    所谓推送通知,用老爷爷都能听懂的话说,就是: 1.我的服务器将通知内容发送到微软的通知服务器,再由通知服务器帮我转发消息. 2.那么,微软的推送服务器是如何知道我的服务器要发消息给哪台手机呢?手机客户 ...

  5. HTML5之废弃和更新的元素与属性

    废弃的元素和属性 [1]标签替换 <acronym> 替代:<abbr> <applet> 替代:<embed> 或 <object> &l ...

  6. Oracle循环语句

    PL/SQL有四种类型的循环:简单循环.WHILE循环.FOR循环以及游标FOR循环.在这里我们主要讨论前三种,除此之外,还将讨论Oracle 11g中新引入的CONTINUE语句. 一. 简单循环 ...

  7. Visual Studio 选择相同变量高亮

    前段时间一直在使用matlab,今天需要使用vs2008,而用惯了matlab,习惯了其中一项选中变量高亮的设置,突然回来使用VS,感到各种不适应,顿时想到了一个词:矫情 呵呵,于是在网上找各种插件, ...

  8. Docker的学习--介绍和安装

    什么是 Docker Docker 是一个开源项目,诞生于 2013 年初,最初是 dotCloud 公司内部的一个业余项目.它基于 Google 公司推出的 Go 语言实现. 项目后来加入了 Lin ...

  9. android 处理302地址

    最近项目中需要用到重定向下载,所以找了很多的方法都不合适.因为下载的链接并非单纯的地址,而是需要多次转发的, 在下载的时候用的是URL来打开数据流.但是多次测试并不能对多次跳转的链接打开请求.对于30 ...

  10. get与post需要注意的几点

    在面试或者笔试时,经常会被问到 HTTP 方法中 get 和 post 的异同点.本文简单整理归纳了一下,以备忘. 1."get/post" VS "web 中的 get ...