来总结一下HashMap的原理

1.HashMap当中有一个内部类,它叫Node,然后这个Node呢,它其实是实现了Map.Entry接口,这个接口当中有几个抽象的方法和几个具体的方法。其中Map.Entry<K,V>是一个泛型的元组。

2.Map.Entry接口中有如下抽象方法:

  • getKey()
  • getValue()
  • setValue()
  • hashCode()
  • equals

3.Node的私有变量如下:

  • hash
  • key
  • value
  • Next node

其中HashMap的核心是hashcode的生成算法,hashCode的生成算法如下:

 Objects.hashCode(key) ^ Objects.hashCode(value);

它是先通过得到Key和value的hashcode,然后对2个值进行异或操作后得到的值。

其中Object.hashCode是一个native的方法。

    public native int hashCode();

其中Node的equals方法,传入的对象是object,只有当object的类型是map.entry并且,当前对象的key和value都和传入的key,value一致,那样才会返回相等。

下面的这个方法,是计算hash值的方法。它是通过key去计算,然后把拿到的hashcode和它右移16位的结果进行异或操作,具体回头再看为什么,我也不知道。

 static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}

hashMap里面还有一个entrySet的成员变量,它是一个set 的集合,这里面的transient关键字不太懂,回头再看看。

   transient Set<Map.Entry<K,V>> entrySet;

HashMap里面有一个非常重要的方法,叫做putVal()方法。这算是里面最核心的一个方法了,弄懂了这个方法,80%的HashMap相关的知识都能弄懂了

首先是有2个Node的声明,一个是tab,一个是p.

Node<K,V>[] tab; Node<K,V> p; int n, i;

下面我们来解析一下PutVal方法,如果table为空,或者table的长度为0,重置table的长度。

if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length;

首先是下面的代码会利用到上面的代码,n得出了一个结果,那就是resize()后的结果,下面的n-1就是“”最后“”一个元素

        if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);

下面的代码就是上面的tabl[x]里面的逻辑,这里面用到了按位与的一个运算:为什么要这么做?不知道。

来做一个小小的补充,这里要先复习一下按位与的结果操作,什么时候获取什么值,如果hash是一个负数,那又是什么情况呢?

(n - 1) & hash

我猜想的是,如果“”找到的“”元素为null,那么新建一个node元素。并且这个node元素的next为空。否则执行else里面的逻辑。

----------------------------------------我是分割线---------------------------------------------

首先putVal方法会去计算这个key的hash值。

首先我觉得要明白hash算法的真谛,网上找的这句话,说得不错:要找到散列为同一个值的两个不同的输入,在计算上是不可能的,所以数据的哈希值可以检验数据的完整性。

当第一次进入putval方法的时候,table是空的,所以肯定要进行一个resize操作,不光是table,连threshold都是0,所有的东西都未能初始化的情况下,这个时候,应该进入如下逻辑:

newCap = DEFAULT_INITIAL_CAPACITY;newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);

这个时候,newCapicity的话就变成了初始值16,newThr变成了初始化的Threhold,如果是这种情况下的话,就会新建一个长度为16的Node<K,V>[]数组,最后返回newTable,注意,resize操作的返回对象。

   Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];

在下面的例子中,hash值是一个非常大的值,换算成2进制,它是32位长度的一个2进制,用按位与的操作是最快的,因为计算机内部结构就是二进制的。

 if ((p = tab[i = (n - 1) & hash]) == null)

注意!!!它先填充的是tab[10]的内容,也就是说,并没有从0开始填充,这是违背我们直觉的一件事情。

然后让modCount++,

最后,如果负载因子小于size,那么,hashmap会自动扩容。

        if (++size > threshold)resize();

随后执行afterNodeInsertion方法,这个方法在Hashmap当中是一个空的方法,API里面介绍的是为LinkedHashMap所用,所以这里不再做讨论。还有注意下,如果是新建的hashMap第一次putval,那么它的返回值为null.

要注意一点,新建一个HashMap,它并不是独立存在的,在你把你的key添加进去之前,它还会添加非常多的其他的KEY,也就是我们所说的:系统路径,所以最后得出的结果就是,如果你是一个新的HashMap,那么,你添加了一个KEY,肯定这里面不止一个KEY。

大家可以观察到,当SIZE=13的时候,

其实是自动进入了resize这个方法的,你看我断点都进来了。这就证明了hashmap的自动扩容机制。

那么为什么会有这么多的Node被添加进来呢?原因只有一个,就是我们用idea启动项目的时候,一些类其实是用到了HashMap的,它优于我们调试的时候进入的HashMap,所以刚才大家才会看到那么多的节点被添加到hashMap当中去。

当我把在putVal上的断点去掉以后,就进入了如下代码块,验证了我的猜想。

另外还有一个很有趣现象,我用单元测试,新建了一个HashMap,结果。。。你发现没有,jdk里面已经填充了4项了,原来,我们认为的Hashmap,有多少项,就add多少项的观点其实是错误的!!!

下面我们再来看看如下代码,传入的hash和之前的hash进行对比,这里面可能大家有一些迷糊,当然包括我也看不懂,不过从这里可以获取一个非常重要的信息,这么做的方式就是为了避免一个hashmap钟可能出现“相同”的hash对象,我是这么理解的,如果有高人,可以来解释下为什么这样。

        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);

下面的代码的含义是,如果p.next为空的话,那么新建一个节点,并追加到尾部,这种情况,就是当p指向最后一个节点的时候才会出现的情况。

                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;}

后面的代码有点感觉太难啃了,先暂时就这样吧,今天的HashMap分析得还不太完整,并且不太合理,希望大家能多提宝贵建议。

转载于:https://www.cnblogs.com/kmsfan/p/8027184.html

HashMap原理总结相关推荐

  1. hashmap原理_想要彻底搞懂HashMap?你得恶补下HashMap原理

    引言 唉! 金九银十转眼已过, 面试现场不知所措: 不懂原理是我过错, 今日弥补只为求过. ====================================================== ...

  2. 【Java基础】HashMap原理详解

    [Java基础]HashMap原理详解 HashMap的实现 1. 数组 2.线性链表 3.红黑树 3.1概述 3.2性质 4.HashMap扩容死锁 5. BATJ一线大厂技术栈 HashMap的实 ...

  3. Java基础-hashMap原理剖析

    Java基础-hashMap原理剖析 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任.   一.什么是哈希(Hash) 答:Hash就是散列,即把对象打散.举个例子,有100000条数 ...

  4. 【图解版】HashMap原理初探

    参考博客 Java中的equals和hashCode方法详解 链表 java提高篇(二三)-–HashMap Java HashMap的工作原理 算法的时间复杂度和空间复杂度-总结 Hashmap H ...

  5. HashMap原理以及TreeMap和Collections工具类(2022.6.10)

    HashMap原理 当你往Map集合中添加元素的时,它的底层是怎样实现的? Map<String, Integer> hs = new HashMap<>();hs.put(& ...

  6. java基础--java中HashMap原理

    java中HashMap原理 内推军P21 P22 1.为什么用HashMap? HashMap是一个散列桶(数组和链表),它存储的内容是键值对(key-value)映射HashMap采用了数组和链表 ...

  7. Android面试题--HashMap原理分析

    目录 一.序言 二 .HashMap原理分析 二.HashMap和Hashtable区别? 一.序言 作为Android程序员,出去找工作面试,HashMap应该是最常被问到的一种数据类型.那它是怎么 ...

  8. 字节跳动Android三面视频解析:framework+MVP架构+HashMap原理+性能优化+Flutter+源码分析等

    前言 对于字节跳动的二面三面而言,Framework+MVP架构+HashMap原理+性能优化+Flutter+源码分析等问题都成高频问点!然而很多的朋友在面试时却答不上或者答不全!今天在这分享下这些 ...

  9. 面试官:说一下HashMap原理,循环链表是如何产生的

    Map 这样的 Key Value 在软件开发中是非常经典的结构,常用于在内存中存放数据.众所周知 HashMap 底层是基于 数组 + 链表 组成的,不过在 JDK1.7 和 1.8 中具体实现稍有 ...

  10. 【多图预警,不懂来敲我】图说HashMap原理和流程

    1.前言 HashMap是开发中最常用的键值对集合类,也是面试中经常被问及的一个知识点,也是衡量java基础是否扎实的标准之一,是每个JAVA初学者入门必须跨过的槛.所以HashMap重要性不言而喻, ...

最新文章

  1. 再谈PowerPoint 2010导出幻灯片为图片
  2. find命令查找某些文件并将其拷贝到指定目录
  3. Solr学习总结(一)Solr介绍
  4. 数据产品经理:6大数据分析平台的“世界观”
  5. 微信圈子将于12月28日停止运营,网友:不是微信朋友圈?
  6. 平面设计师必备素材|中国/国潮风格
  7. linux命令dh f,linux 下 find 命令的高级用法
  8. Jenkins中的一些问题解决(~~不断更新~~)
  9. Java判断浏览器类型
  10. 脚本录制软件python 按键精灵 tc_Keymouse Go鼠标键盘脚本录制下载|开源版按键精灵软件_最火软件站...
  11. 一个专门下载全球气象站数据的网站(包括中国700多个站)
  12. 我的世界java版骨头指令_我的世界:如何调出“啃骨头”隐藏皮肤?1个没人知道的mc彩蛋...
  13. 关于Python入门的常用工具Wing你了解多少
  14. Python3爬取淘宝网商品数据!
  15. 《货币金融学》第七版
  16. 毗邻华尔街,哥伦比亚大学、纽约大学如何将金融科技的理论与实践结合?
  17. esp8266网络自动对时 串口字符连接 病显示 12864i2c u8g2库
  18. Python 实现摄像头功能
  19. 鸿蒙开发实例 | 分布式涂鸦
  20. 「Arm Arch」 微架构

热门文章

  1. ls –al命令来观察文件的权限,每个文件的权限都用10位表示,并分为四段!
  2. qdbus 复杂类型
  3. 使用xshell远程连接Linux
  4. 浅析大数据时代下,全球医疗信息技术将出现暴增
  5. lamp wordpress
  6. [转] form.getForm().submit的用法及Ext.Ajax.request的小小区别
  7. String类中toCharArray()方法的用法
  8. mysql主从库配置方法
  9. C++ sizeof与strlen
  10. 华为AC6605与OSSH免费版华为Portal系统的对