作者:阿进的写字台 来源:https://www.cnblogs.com/homejim/p/10029796.html

1、HashMap在JAVA中的怎么工作的?

基于Hash的原理。

2、什么是哈希?

最简单形式的 hash,是一种在对任何变量/对象的属性应用任何公式/算法后, 为其分配唯一代码的方法。

一个真正的hash方法必须遵循下面的原则。

哈希函数每次在相同或相等的对象上应用哈希函数时, 应每次返回相同的哈希码。换句话说, 两个相等的对象必须一致地生成相同的哈希码。

Java 中所有的对象都有 Hash 方法。

Java中的所有对象都继承 Object 类中定义的 hashCode() 函数的默认实现。 此函数通常通过将对象的内部地址转换为整数来生成哈希码,从而为所有不同的对象生成不同的哈希码。

3、你清楚HashMap 中的 Node 类的结构吗?

Map的定义是: 将键映射到值的对象。

因此,HashMap 中必须有一些机制来存储这个键值对。 答案是肯的。 HashMap 有一个内部类 Node,如下所示。

当然,Node 类具有存储为属性的键和值的映射。 key 已被标记为 final,另外还有两个字段:next 和 hash。

在下面中, 我们将会理解这些属性的必须性。

4、键值对在 HashMap中是如何存储的?

键值对在 HashMap 中是以 Node 内部类的数组存放的,如下所示。

transient Node[] table;

哈希码计算出来之后, 会转换成该数组的下标, 在该下标中存储对应哈希码的键值对, 在此先不详细讲解hash碰撞的情况。

该数组的长度始终是2的次幂, 通过以下的函数实现该过程。

其原理是将传入参数 (cap) 的低二进制全部变为1,最后加1即可获得对应的大于 cap 的 2 的次幂作为数组长度。

为什么要使用2的次幂作为数组的容量呢?

在此有涉及到 HashMap 的 hash 函数及数组下标的计算, 键(key)所计算出来的哈希码有可能是大于数组的容量的,那怎么办? 可以通过简单的求余运算来获得,但此方法效率太低。HashMap中通过以下的方法保证 hash 的值计算后都小于数组的容量。

(n - 1) & hash

这也正好解释了为什么需要2的次幂作为数组的容量。由于n是2的次幂,因此,n-1类似于一个低位掩码。通过与操作,高位的hash值全部归零,保证低位才有效 从而保证获得的值都小于n。

同时,在下一次 resize() 操作时, 重新计算每个 Node 的数组下标将会因此变得很简单,具体的后文讲解。以默认的初始值16为例。

 01010011 00100101 01010100 00100101& 00000000 00000000 00000000 00001111---------------------------------- 00000000 00000000 00000000 00000101 //高位全部归零,只保留末四位 // 保证了计算出的值小于数组的长度 n

但是,使用了该功能之后,由于只取了低位,因此 hash 碰撞会也会相应的变得很严重。这时候就需要使用「扰动函数」。

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

该函数通过将哈希码的高16位的右移后与原哈希码进行异或而得到,以上面的例子为例。

此方法保证了高16位不变, 低16位根据异或后的结果改变。计算后的数组下标将会从原先的5变为0。

使用了 「扰动函数」 之后, hash 碰撞的概率将会下降。 有人专门做过类似的测试, 虽然使用该 「扰动函数」 并没有获得最大概率的避免 hash 碰撞,但考虑其计算性能和碰撞的概率, JDK 中使用了该方法,且只hash一次。

5、哈希碰撞是如何处理的?

在理想的情况下, 哈希函数将每一个 key 都映射到一个唯一的 bucket, 然而, 这是不可能的。哪怕是设计在良好的哈希函数,也会产生哈希冲突。

前人研究了很多哈希冲突的解决方法,在维基百科中,总结出了四大类。

在 Java 的 HashMap 中, 采用了第一种 Separate chaining 方法(大多数翻译为拉链法)+链表和红黑树来解决冲突。

在 HashMap 中, 哈希碰撞之后会通过 Node 类内部的成员变量 Node next; 来形成一个链表(节点小于8)或红黑树(节点大于8, 在小于6时会从新转换为链表), 从而达到解决冲突的目的。

static final int TREEIFY_THRESHOLD = 8;static final int UNTREEIFY_THRESHOLD = 6;

6、HashMap 是如何初始化的?

 public HashMap(); public HashMap(int initialCapacity); public HashMap(Map extends K, ? extends V> m); public HashMap(int initialCapacity, float loadFactor); 

HashMap 中有四个构造函数, 大多是初始化容量和负载因子的操作。以 public HashMap(int initialCapacity, float loadFactor) 为例。

通过该函数进行了容量和负载因子的初始化,如果是调用的其他的构造函数, 则相应的负载因子和容量会使用默认值(默认负载因子=0.75, 默认容量=16)。在此时, 还没有进行存储容器 table 的初始化, 该初始化要延迟到第一次使用时进行。

7、HashMap 中哈希表是如何动态扩容的?

所谓的哈希表, 指的就是下面这个类型为内部类Node的 table 变量。

transient Node[] table;

作为数组, 其在初始化时就需要指定长度。在实际使用过程中, 我们存储的数量可能会大于该长度,因此 HashMap 中定义了一个阈值参数(threshold), 在存储的容量达到指定的阈值时, 需要进行扩容。

我个人认为初始化也是动态扩容的一种, 只不过其扩容是容量从 0 扩展到构造函数中的数值(默认16)。 而且不需要进行元素的重hash.

7.1 扩容发生的条件

初始化的话只要数值为空或者数组长度为 0 就会进行。 而扩容是在元素的数量大于阈值(threshold)时就会触发。

threshold = loadFactor * capacity

比如 HashMap 中默认的 loadFactor=0.75, capacity=16, 则。

threshold = loadFactor * capacity = 0.75 * 16 = 12

那么在元素数量大于 12 时, 就会进行扩容。 扩容后的 capacity 和 threshold 也会随之而改变。

负载因子影响触发的阈值,因此,它的值较小的时候,HashMap 中的 hash 碰撞就很少, 此时存取的性能都很高,对应的缺点是需要较多的内存;而它的值较大时,HashMap 中的 hash 碰撞就很多,此时存取的性能相对较低,对应优点是需要较少的内存;不建议更改该默认值,如果要更改,建议进行相应的测试之后确定。

7.2 再谈容量为2的整数次幂和数组索引计算

前面说过了数组的容量为 2 的整次幂, 同时, 数组的下标通过下面的代码进行计算。

index = (table.length - 1) & hash

该方法除了可以很快的计算出数组的索引之外, 在扩容之后, 进行重 hash 时也会很巧妙的就可以算出新的 hash 值。 由于数组扩容之后, 容量是现在的 2 倍, 扩容之后 n-1 的有效位会比原来多一位, 而多的这一位与原容量二进制在同一个位置。 示例。

这样就可以很快的计算出新的索引啦。

7.3 步骤

  • 先判断是初始化还是扩容, 两者在计算newCap和newThr时会不一样
  • 计算扩容后的容量,临界值。
  • 将hashMap的临界值修改为扩容后的临界值
  • 根据扩容后的容量新建数组,然后将hashMap的table的引用指向新数组。
  • 将旧数组的元素复制到table中。在该过程中, 涉及到几种情况, 需要分开进行处理(只存有一个元素, 一般链表, 红黑树)

具体的看代码吧。

7.4 注意事项

虽然 HashMap 设计的非常优秀, 但是应该尽可能少的避免 resize(), 该过程会很耗费时间。

同时, 由于 hashmap 不能自动的缩小容量 因此,如果你的 hashmap 容量很大,但执行了很多 remove操作时,容量并不会减少。如果你觉得需要减少容量,请重新创建一个 hashmap。

8、HashMap.put() 函数内部是如何工作的?

在使用多次 HashMap 之后, 大体也能说出其添加元素的原理:计算每一个key的哈希值, 通过一定的计算之后算出其在哈希表中的位置,将键值对放入该位置,如果有哈希碰撞则进行哈希碰撞处理。

而其工作时的原理如下。

源码如下。

在此过程中, 会涉及到哈希碰撞的解决。

9、HashMap.get() 方法内部是如何工作的?

其最终是调用了 getNode 函数。 其逻辑如下。

源码如下。

hashmap remove 没释放内存_面试题:来,问你几个关于HashMap的问题?相关推荐

  1. hashmap remove 没释放内存_java从零开始手写 redis(13)HashMap 源码原理详解

    为什么学习 HashMap 源码? 作为一名 java 开发,基本上最常用的数据结构就是 HashMap 和 List,jdk 的 HashMap 设计还是非常值得深入学习的. 无论是在面试还是工作中 ...

  2. hashmap是有序还是无序_说实话,你要是看完这篇 HashMap ,和面试官扯皮真的就没问题了!

    文章来源:看完这篇 HashMap ,和面试官扯皮就没问题了 原文作者:cxuan 来源平台:微信公众号 (如果你没有时间细抠本文,可以直接看 HashMap 概述,能让你对 HashMap 有个大致 ...

  3. 为什么要free释放内存_为什么在Free Code Camp上列出一份工作要花1,000美元?

    为什么要free释放内存 by Michael D. Johnson 迈克尔·约翰逊(Michael D.Johnson) 为什么在Free Code Camp上列出一份工作要花1,000美元? (W ...

  4. linux std::queue 怎么释放内存_电脑卡慢怎么办?一个小工具帮你轻松释放内存,瞬间提升电脑性能...

    有一种电脑叫"卡巴死机" 大家有没有发现,如今的电子产品越来越不耐用了. 无论是电脑,还是手机,超过一年以上,就得考虑更换了. 1G变2G,2G升4G,按理说电脑应该会更快更好,实 ...

  5. 苹果删除照片不释放内存_手机内存不够用?删除这3个“特殊”文件夹,立刻释放大量空间!...

    阅读本文前,请您先点击上面的蓝字,再点击"关注",这样您就可以免费收到最新内容了.每天都有分享,完全是免费订阅,请放心关注.1111111111111111111111111111 ...

  6. python读取数据库数据释放内存_在使用python处理数据时,为什么其内存无法自动释放掉?...

    这与Python的内存处理机制有关. 我们对内存一定要合理利用,这是每一个程序员必须的基本功.减少垃圾排放,这才是根本,所以每一个程序员他必须得知道自己用了多少内存.别自己这些内存要不要释放,我为什么 ...

  7. python processpoolexector 释放内存_关于python:如何在multiprocessing.queue中从Process中释放内存?...

    我有一个程序试图预测一周内发送的每封电子邮件的电子邮件转换(因此,通常是7封). 输出是7个不同的文件,每个客户的预测得分. 串行运行这些可能需要8个小时,因此我尝试使用multiprocessing ...

  8. python 释放内存_学了4年C++后,我转向了Python

    作者 | asya f 编译 | Lisa C++ 已经学不动了,现在换 Python 还来得及吗?一位四年工作经验的 C++ 程序员亲述转型历程,这不仅仅是语言上的转变,而是代码思维甚至工作环境的转 ...

  9. qt 如何 指针 自动 释放内存_要是面试官再问你智能指针的问题,就拿这篇文章“盘他”!!!...

    前一段时间,有不少朋友问我关于智能指针的问题,并且反映经常会在面试中被面试官问到,所以今天小豆君就来讲讲我对智能指针的理解,希望能对大家有所帮助 既然讲智能指针,我们就先来看看它为什么会出现. 1 传 ...

最新文章

  1. 对抗 Google优势 微软考虑收购雅虎
  2. android常见错误-Installation error: INSTALL_FAILED_INSUFFICIENT_STORAGE
  3. asp.net MVC 的处理流程
  4. 计算机学院可以表演的小品,中央戏剧学院小品表演真题之单人小品
  5. oracle 9i乱码,oracle 9i sql*plus worksheet乱码问题
  6. 求助:关于sql如何统计时间的问题
  7. 带有Prometheus的弹簧靴和千分尺第5部分:旋转Prometheus
  8. vim 实用技巧 查找和替换
  9. Mysql学习总结(72)——MySQL 开发者开发,设计规范再总结
  10. 【CNN】94页论文综述卷积神经网络:从基础技术到研究前景
  11. JAVA----数组(一)
  12. ABAP新手基础入门知识
  13. 机顶盒怎样配置服务器信息,网络机顶盒桌面配置服务器
  14. 电脑宝马,大量宝马车电脑通病故障检修方法
  15. python钻石和玻璃球游戏_python使用pygame实现笑脸乒乓球弹珠球游戏
  16. 手把手教你用VMware安装Centos7.9镜像(史上最详细)
  17. java list map 去重复_Java中List集合去除重复数据的六种方法
  18. 2022谈谈手机充电头/快充头/电源适配器
  19. 统一告警平台设计方案
  20. 解决usage: mo_onnx.py [options] mo_onnx.py: error: unrecognized arguments: 113.985 119.85] 69.87 70.89

热门文章

  1. 为TIF、JPG图片添加地理坐标/平面直角坐标
  2. 封装SQLDMO操作的类
  3. sql server 中将datetime类型转换为date,或者time
  4. 【CyberSecurityLearning 63】CSRF攻击
  5. 操作系统(十一)线程的概念和特点
  6. STM32开发 -- 信号强度CSQ
  7. C语言再学习 -- 运算符与表达式
  8. 请问知识图谱有哪些研究点是可以进行深入研究的?
  9. Making Sense of Cryptoeconomics
  10. 目前区块链项目的生态系统:一共七大类