前言:通常大家都知道HashMap的底层数据结构为数组加链表的形式,但其put操作具体是怎样执行的呢,本文通过调试HashMap的源码来阐述这一问题。

注:jdk版本:jdk1.7.0_51


1.put源码

 public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
if (key == null)
return putForNullKey(value);
int hash = hash(key);
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
} modCount++;
addEntry(hash, key, value, i);
return null;
}

下面通过对源码调试具体说明put操作的流程。

2.具体调试过程

 public class CodeTest04
{
public static void main(String[] args)
{
String keyAa = "Aa";
String keyBB = "BB"; System.out.println("keyAa hashCode=" + keyAa.hashCode());
System.out.println("keyBB hashCode=" + keyBB.hashCode()); HashMap<String, String> hashMap = new HashMap<String, String>(); hashMap.put(keyAa, "abc");
// hashMap.put(keyBB, "abc");
hashMap.put(keyAa, "cba");
hashMap.put(keyAa,"def"); System.out.println(hashMap); }
}

说明:

不同内容的字符串,其hashCode是可能相等的。如字符串“Aa”和“BB”的hashCode相等(都是2112)。

②当执行上述代码13行和15、16行的时候,结果如下:

③当执行上述代码13行和14行时,结果如下:

通过上述结果,可得出如下结论:

①在key的hashCode相等,且key也相同的情况下,HashMap在执行put操作的时候,会进行覆盖操作,新值将覆盖旧值,所以最后输出为"{Aa=def}"。

②在key的hashCode相等,但key不相同(如"Aa"与"BB")的情况下,HashMap在执行put操作的时候,就会进行链表式存储,并且采用的是头插法,即新值处于链表头,最开始的值处于链表尾

#1.对于第一个结论,通过下面这段源码可以很清晰的了解其流程:

#2.对于第二个结果,key的hashCode相等,但key不同的情况,通过调试描述put的过程。

通过debug可以非常清晰的看出:当前hashmap中存在一对键值{"Aa"->"abc"},其key的hash值为2112,在put("BB","abc")的时候,发现其hash值也是2112,出现上文中描述的情况(key的hashCode值相等,当key的内容不同),因此会直接跳转。

注意i的值,这里的i表示put元素在hashmap底层数据结构中的索引值(可简单想象成元素在数组上的索引),因为"Aa"与"BB"的hashCode值相同,因此两次i的值都是。我们进入addEntry函数。

由于我们这里不需要扩容,因此会创建新的元素,注意此时桶的index为4,但是当前位置上已经存在"Aa"的键值对了,进入该函数。

这里可以清晰的看出,会取出之前bucketIndex=4上的键值对,也就是{"Aa"->"abc"},然后new一个Entry。

关键过程:把当前bucketIndex=4的值替换成了{"BB"->"abc"},并将其next的值赋值为{"Aa"->"abc"},这里就完成了头插法

总结

HashMap在进行put操作时,会有如下操作:

1)在key的hashCode相等,且key的内容也相同的情况下(两次"Aa"),就会对value值进行覆盖

2)在key的hashCode相等,但key的内容不相同的情况下("Aa","BB"),会进行链式存储,并且是头插法,新加的值("BB")在链表头,最开始("Aa")的值在链表尾。

3)在key的hashCode不相等的情况,直接进行散列存储。

4)从上述调试过程也发现,HashMap主要是以key为主,value相当于key的一个附属值,因为value随key走的。


by Shawn Chen,2018.6.8日,晚。

HashMap源码调试——认识"put"操作的更多相关文章

  1. HashMap源码分析(一)

    前言:相信不管在生产过程中还是面试过程中,HashMap出现的几率都非常的大,因此有必要对其源码进行分析,但要注意的是jdk1.8对HashMap进行了大量的优化,因此笔者会根据不同版本对HashMa ...

  2. HashMap 源码解析

    HashMap简介: HashMap在日常的开发中应用的非常之广泛,它是基于Hash表,实现了Map接口,以键值对(key-value)形式进行数据存储,HashMap在数据结构上使用的是数组+链表. ...

  3. JAVA源码分析-HashMap源码分析(一)

    一直以来,HashMap就是Java面试过程中的常客,不管是刚毕业的,还是工作了好多年的同学,在Java面试过程中,经常会被问到HashMap相关的一些问题,而且每次面试都被问到一些自己平时没有注意的 ...

  4. Java集合---HashMap源码剖析

    一.HashMap概述二.HashMap的数据结构三.HashMap源码分析     1.关键属性     2.构造方法     3.存储数据     4.调整大小 5.数据读取           ...

  5. 在Eclipse中进行HotSpot的源码调试--转

    原文地址:http://www.linuxidc.com/Linux/2015-05/117250.htm 在阅读OpenJDK源码的过程中,经常需要运行.调试程序来帮助理解.我们现在已经可以编译出一 ...

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

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

  7. 【JAVA集合】HashMap源码分析(转载)

    原文出处:http://www.cnblogs.com/chenpi/p/5280304.html 以下内容基于jdk1.7.0_79源码: 什么是HashMap 基于哈希表的一个Map接口实现,存储 ...

  8. HashMap源码解读(转)

    http://www.360doc.com/content/10/1214/22/573136_78188909.shtml 最近朋友推荐的一个很好的工作,又是面了2轮没通过,已经是好几次朋友内推没过 ...

  9. HashMap源码剖析

    HashMap源码剖析 无论是在平时的练习还是项目当中,HashMap用的是非常的广,真可谓无处不在.平时用的时候只知道HashMap是用来存储键值对的,却不知道它的底层是如何实现的. 一.HashM ...

随机推荐

  1. git常用命令总结(资源来自廖雪峰)

    自己把命令弄出来方便以后看看,,应该有错的emmmm 原文地址:https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67 ...

  2. TeamViewer 12\13\14 破解版(解决检测为商业用途的方式)

    一.Windows系统下破解TeamViewer的方式 1.用Windows直接卸载本地的TeamViewer软件2.下载一个Everything软件,并安装好它(这是一个搜索本机文件的工具,超级好用 ...

  3. Netty 系列七(那些开箱即用的 ChannelHandler).

    一.前言 Netty 为许多通用协议提供了编解码器和处理器,几乎可以开箱即用, 这减少了你在那些相当繁琐的事务上本来会花费的时间与精力.另外,这篇文章中,就不涉及 Netty 对 WebSocket协 ...

  4. Javascript继承6:终极继承者----寄生组合式继承

    /* * 寄生式继承依托于原型继承,原型继承又与类式继承想象. * 即: 原型与构造函数的组合继承 * 寄生式继承 继承原型 * 传递参数 childClass 子类 * 传递参数 parentCla ...

  5. git入门 创建版本库, 版本管理 分支 标签

    参考: https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000 GIT最流行的分布式版本 ...

  6. Java中static与final

    修饰变量:static:静态变量,是属于这个类的final :常量,只能赋值一次static final:静态常量,必须立即初始化(同时具有static.final的特点) 修饰方法:static:静 ...

  7. jQuery 练习:取出数组字典的值, 静态对话框, clone方法应用

    jQuery 中文API文档 http://jquery.cuishifeng.cn/ jQuery 取出数组字典的值 <head> <meta charset="UTF- ...

  8. JavaScript之Number、String、Array常用属性与方法手册

    Number isFinite函数 Number.isFinite() 方法用来检测传入的参数是否是一个有穷数(finite number). 语法: Number.isFinite(value) 例 ...

  9. Android 四大组件之broadcast的理解

    Android广播的两种类型: 1.静态广播 2.动态广播 静态注册广播: Manifeast中的代码块: <receiver android:name=".broadcast.MyS ...

  10. 章节七、1-ArrayList

    一.集合是一个容器,前面讲的数值也是一个容器, 它们的区别是: 1.数组既可以存储基本数据类型,又可以存储引用数据类型,而集合只能存储引用数据类型,也就是对象. 2.基本数据类型存储的是值,引用数据类 ...