通过Debug 探寻Java-HashMap 实现原理:

一个简单的例子,代码如下,

测试方法 main:

     public static void main(String[] args) {

         KeyObj obj1 = new KeyObj("AAAA");
KeyObj obj2 = new KeyObj("BBBB");
KeyObj obj3 = new KeyObj("CCCCC");
KeyObj obj4 = new KeyObj("DDDDD"); HashMap<KeyObj, String> hashMap = new HashMap<KeyObj, String>();
hashMap.put(obj1, "aaaa");
hashMap.put(obj2, "bbbb");
hashMap.put(obj3, "ccccc");
hashMap.put(obj4, "ddddd"); System.out.println(hashMap.values()); }

KeyObj.java

 public class KeyObj {

     String keyStr;

     public KeyObj(String keyStr) {
super();
this.keyStr = keyStr;
} public String getKeyStr() {
return keyStr;
} public void setKeyStr(String keyStr) {
this.keyStr = keyStr;
}
  
@Override
    // 覆盖hashCode方法,使得key的单双数分别获得一致的hash值,方便测试
public int hashCode() {
if (keyStr.length() % 2 == 0) {
return 31;
}
return 95;
} @Override
public boolean equals(Object obj) {
KeyObj obj1 = (KeyObj) obj;
if (this.keyStr.equalsIgnoreCase(obj1.keyStr))
return true;
return false;
} }

运行测试方法main,Debug查看HashMap:

1.可以看到HashMap其实是有一个名称为table的Entry数组,我们使用HashMap的put方法,本质是把我们的Key-Value作为Entry对象放入到HashMap中。

2.HashMap的table数组初始大小为16.

3.为何我们put了4个对象却只使用了table[10] 与 table[14]?

查看put方法JDK代码:

     public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold); // 3.1 table若为空创建table,16大小
}
if (key == null)
return putForNullKey(value); // 3.2 key若为空放入table[0]
int hash = hash(key); // 3.3 计算放入key的hash,值为调用KeyObj的hashcode方法,再hash
int i = indexFor(hash, table.length);
       // 3.4 计算当前put的Enrty在table数组中精确位置(int i),跟踪代码可以很容易看出:
       // 3.4.1 精确位置i是由key的hash值与table.length取模
for (Entry<K,V> e = table[i]; e != null; e = e.next) { // 3.5 遍历table[i]的Entry
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { // 3.6 若put相同key的KeyObj,则替换旧值
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
} modCount++;
addEntry(hash, key, value, i);// 3.7 创建Entry,当前table[i]的首节点变为当前put节点Entry对象的next节点,注:JDK8之前每一个table[i]上的Entry使用单链表存储的
return null;
}

理解了put,查看HashMap的get方法就很简单了:

     public V get(Object key) {
if (key == null)
return getForNullKey();
Entry<K,V> entry = getEntry(key); return null == entry ? null : entry.getValue();
}

继续查看第四行getEntry方法:

     final Entry<K,V> getEntry(Object key) {
if (size == 0) {
return null;
} int hash = (key == null) ? 0 : hash(key);
for (Entry<K,V> e = table[indexFor(hash, table.length)]; // 找到table[i],并便利该位置节点
e != null;
e = e.next) {
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) // 找到,返回
return e;
}
return null;
}

其他:

在put函数代码中有一个注释:

JDK8之前每一个table[i]上的Entry使用单链表存储的

一直到JDK7为止,HashMap的结构都是这么简单,基于一个数组以及多个链表的实现,hash值冲突的时候,就将对应节点以链表的形式存储。

这样子的HashMap性能上就抱有一定疑问,如果说成百上千个节点在hash时发生碰撞,存储一个链表中,那么如果要查找其中一个节点,那就不可避免的花费O(N)的查找时间,这将是多么大的性能损失。这个问题终于在JDK8中得到了解决。再最坏的情况下,链表查找的时间复杂度为O(n),而红黑树一直是O(logn),这样会提高HashMap的效率。

所以在JDK8中,当同一个hash值的节点数不小于8时,将不再以单链表的形式存储了,会被调整成一颗红黑树。

Java的HashMap实现原理整理总结的更多相关文章

  1. 【JAVA】HashMap的原理及多线程下死循环的原因

    再次翻到以前工作中遇到的一个问题,HashMap在多线程下会出现死循环的问题,以前只是知道会死循环,导致CPU100%把机器拖跨,今天来彻底看看 首先来看下,HashMap的原理:HashMap是一个 ...

  2. Java面试& HashMap实现原理分析

    1. HashMap的数据结构 数据结构中有数组和链表来实现对数据的存储,但这两者基本上是两个极端.  数组 数组存储区间是连续的,占用内存严重,故空间复杂的很大.但数组的二分查找时间复杂度小,为O( ...

  3. 详解 Java 8 HashMap 实现原理

    HashMap 是 Java 开发过程中常用的工具类之一,也是面试过程中常问的内容,此篇文件通过作者自己的理解和网上众多资料对其进行一个解析.作者本地的 JDK 版本为 64 位的 1.8.0_171 ...

  4. 1.Java集合-HashMap实现原理及源码分析

    哈希表(Hash  Table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,而HashMap的实现原理也常常 ...

  5. 【Java】HashMap实现原理---数据结构

    作为一个程序猿,特别是Java后端的,应该全部人都用过HashMap,也都知道HaspMap是一个用于存储Key-Value键值对的集合.与此同时我们把每一个键值对也叫做 Entry. 而这些Entr ...

  6. Java中HashMap实现原理

    类声明: 概述: 线程不安全: <Key, Value>两者都可以为null: 不保证映射的顺序,特别是它不保证该顺序恒久不变: HashMap使用Iterator: HashMap中ha ...

  7. Java中HashMap的实现原理

    最近面试中被问及Java中HashMap的原理,瞬间无言以对,因此痛定思痛觉得研究一番. 一.Java中的hashCode和equals 1.关于hashCode hashCode的存在主要是用于查找 ...

  8. HashMap的原理与实 无锁队列的实现Java HashMap的死循环 red black tree

    http://www.cnblogs.com/fornever/archive/2011/12/02/2270692.html https://zh.wikipedia.org/wiki/%E7%BA ...

  9. java 关于 hashmap 的实现原理的测试

    网上关于HashMap的工作原理的文章多了去了,所以我也不打算再重复别人的文章.我就是有点好奇,我怎么样能更好的理解他的原理,或者说使用他的特性呢?最好的开发就是测试~ 虽说不详讲hashmap的工作 ...

随机推荐

  1. Sping之Ioc

    Ioc,控制反转,依赖注入! 作用:降低代码之间的耦合性 代码永远是最好的示例: 1.新建test类 package demo; public class test { String string_v ...

  2. 自然饱和度(Vibrance)算法的模拟实现及其SSE优化(附源码,可作为SSE图像入门,Vibrance算法也可用于简单的肤色调整)。

    Vibrance这个单词搜索翻译一般振动,抖动或者是响亮.活力,但是官方的词汇里还从来未出现过自然饱和度这个词,也不知道当时的Adobe中文翻译人员怎么会这样处理.但是我们看看PS对这个功能的解释: ...

  3. jquery 禁止herf跳转,并执行相应的js代码

    <a class="oh_btn bg_3" href="javascript:void(0);" onclick="myfun(this)&q ...

  4. 【基础网络】TCP与UDP 的区别

    TCP协议与UDP协议的区别    首先咱们弄清楚,TCP协议和UCP协议与TCP/IP协议的联系,很多人犯糊涂了,一直都是说TCP/IP协议与UDP协议的区别,我觉得这是没有从本质上弄清楚网络通信! ...

  5. Yii框架用ajax提交表单时候报错Bad Request (#400): Unable to verify your data submission.

    提交表单报400错误,提示 "您提交的数据无法验证"原来是csrf验证的问题,因为表单是自己写的,在Yii框架中,为了防止csrf攻击,对post的表单数据封装了CSRF令牌验证. ...

  6. ajax-javascript原生-初步入门01(整理)

    -----------------------------------2017.07.21写----------------------------------------- 1.ajax的原名: a ...

  7. Uva 10142 Australia Voting

    水题 模拟 大意就是模拟一个选举的系统 认真读题,注意细节,耐心调试 #include<cmath> #include<math.h> #include<ctype.h& ...

  8. canvas的学习

    canvas的学习 一. //获取画布 var mycanvasEle = document.getElementById("mycanvas");二. //内容 var ctx ...

  9. Spring 实现自定义 bean 的扩展

    Spring mvc 提供了扩展 xml 的机制,用来编写自定义的 xml bean ,例如 dubbo 框架,就利用这个机制实现了好多的 dubbo bean,比如 <dubbo:applic ...

  10. Oracle 11.2.0.1 ADG环境MRP进程遭遇ORA-600异常终止

    环境:Linux + Oracle 11.2.0.1 ADG 现象:发现备库没有应用日志 1. 数据库查询备库目前状态 发现备库目前没有应用日志,apply lag已经显示备库有3天21小时多没有应用 ...