面试级解析HashMap
------------恢复内容开始------------
在介绍HashMap之前,有必要先给大家介绍一些参数的概念
HashMap的最大容量,capacity译为容量,capacity就是指HashMap中bucket(桶)的数量。官方给的注解必须为2的幂。默认为1<<4(ps:这里的<<是位移运算符),每次扩容都会扩容为原来的2倍。总之,容量都是2的幂。
1/**
* The default initial capacity - MUST be a power of two.
*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
ps:位移运算符: <<左移运算符,>>右移运算符,还有不带符号的位移运算
计算过程以1<<4为例,首先把1转为二进制数字0000 0001
然后将上面的二进制数字向左移动30位后面补0得到0001 0000
最后将得到的二进制数字转回对应类型的十进制,运行结果为:16
HashMap的最大容量,默认值为1<<30, 即1073741824
/**
* The maximum capacity, used if a higher value is implicitly specified
* by either of the constructors with arguments.
* MUST be a power of two <= 1<<30.
*/
static final int MAXIMUM_CAPACITY = 1 << 30;
public static void main(String[] args) {
int i =1;
System.out.println(i << 30);
}

loadFactor译为装载因子,装载因子用来衡量HashMap的拥挤程度。loadFactor的默认值为0.75f。当HashMap里面容纳的元素已经达到HashMap容量的75%时,表示HashMap太挤了,需要扩容,在HashMap的构造器中可以定制loadFactor。
/**
* The load factor used when none specified in constructor.
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;
树化参数
/**
* 树化阀值,默认值为8
*/
static final int TREEIFY_THRESHOLD = 8;
/**
* 树退化阀值,默认为6
*/
static final int UNTREEIFY_THRESHOLD = 6;
/**
* 最小树化HashMap的capacity(容量),默认为64
*/
static final int MIN_TREEIFY_CAPACITY = 64;
size
HashMap中实际存在的键值对的数量(为链表和树中的KV的总和)
/**
* The number of key-value mappings contained in this map.
*/
transient int size;
threshold(扩容阀值)
threshold表示当HashMap的size大于threshold时会执行resize操作。
threshold=capacity*loadFactor
/**
* The next size value at which to resize (capacity * load factor).
* @serial
*/
int threshold;
接下来,我们通过一场小型面试来探究下HashMap的相关知识:
面试小会场:
面试官:你能跟我聊聊HashMap吗?
小明:HashMap存储键值对实现快速存取,允许为null,key值不可重复,若key值重复则覆盖;非同步,线程不安全;底层是hash表,不保证有序(比如插入的顺序)
面试官:那你能跟我聊聊它的结构和底层原理吗?
小明:HashMap是我们平时常用的数据结构,jdk1.7之前由数组和链表组合构成的数据结构。现在是由数组、链表加红黑树构成的数据结构。数组里面每个地方都存了Key-Value这样的实例,hashMap在put插入数据的时候会根据key去hash计算index的值,如果散列表为空时,调用resize()初始化散列表,如果没有发生hash碰撞,直接添加元素到散列表中去
如图:我们通过哈希函数计算出插入("name","张三")的位置, index = HashCode(Key) & (Length - 1)计算出来的index为2

在有限的数组中,我们使用hash会有一定的概率hash到一个值上
**如果发生了碰撞(hashCode值相同),进行三种判断 **
1.1:若key地址相同或者equals后内容相同,则替换旧值
1.2:如果是红黑树结构,就调用树的插入方法
1.3:链表结构,循环遍历直到链表中某个节点为空,尾插法进行插入,插入之后判断链表个数是否到达变成红黑树的树化阙值 8 ;也可以遍历到有节点与插入元素的哈希值和内容相同,进行覆盖。

所以在HashMap中只能使用引用类型(如Integer、String)来作为key,因为这些类已经很规范的覆写了hashCode()以及equals()方法。当我们去get时,对key的hashCode进行hashing,与运算计算下标获取bucket(桶)位置,如果在bucket桶的首位上就可以找到就直接返回,否则在树中找或者链表中遍历找,如果有hashCode相同,则利用equals方法去遍历链表查找节点
面试官:那你能说一下为什么要引入红黑树吗?
小明:JDK 1.8 以前 HashMap 的实现是 数组+链表,即使哈希函数取得再好,也很难达到元素百分百均匀分布。当 HashMap 中有大量的元素都存放到同一个桶中时,这个桶下有一条长长的链表,这个时候 HashMap 就相当于一个单链表,假如单链表有 n 个元素,遍历的时间复杂度就是 O(n),完全失去了它的优势。针对这种情况,JDK 1.8 中引入了 红黑树(查找时间复杂度为 O(logn))来优化这个问题。
面试官:那你最后在说一下HashMap是如何扩容的吧

小明:如图,假设此时有一个容量为64的HashMap。1号桶内加上挂载的红黑树一共是46个KV,2号桶内加上链表上一共是3个KV,所以该hashmap实例的size = 46 +3 =49。此hashaMap的threshold(扩容阈值)=capacity(当前容量64) * loadfactory(负载因子 0.75) = 48 。由公式++size> load factor * capacity可知此时49>48,所以会扩容。
扩容分为两步
- 扩容:创建一个新的Entry空数组,长度是原数组的2倍。
- ReHash:遍历原Entry数组,把所有的Entry重新Hash到新数组。
ad factor * capacity可知此时49>48,所以会扩容。
扩容分为两步
- 扩容:创建一个新的Entry空数组,长度是原数组的2倍。
- ReHash:遍历原Entry数组,把所有的Entry重新Hash到新数组。
面试官:今天表现不错,等复试吧~
------------恢复内容结束------------
面试级解析HashMap的更多相关文章
- 知名互联网公司校招 Java 开发岗面试知识点解析
天之道,损有余而补不足,是故虚胜实,不足胜有余. 本文作者在一年之内参加过多场面试,应聘岗位均为 Java 开发方向.在不断的面试中,分类总结了 Java 开发岗位面试中的一些知识点. 主要包括以下几 ...
- Java 面试知识点解析(四)——版本特性篇
前言: 在遨游了一番 Java Web 的世界之后,发现了自己的一些缺失,所以就着一篇深度好文:知名互联网公司校招 Java 开发岗面试知识点解析 ,来好好的对 Java 知识点进行复习和学习一番,大 ...
- Java 面试知识点解析(一)——基础知识篇
前言: 在遨游了一番 Java Web 的世界之后,发现了自己的一些缺失,所以就着一篇深度好文:知名互联网公司校招 Java 开发岗面试知识点解析 ,来好好的对 Java 知识点进行复习和学习一番,大 ...
- Java 面试知识点解析(二)——高并发编程篇
前言: 在遨游了一番 Java Web 的世界之后,发现了自己的一些缺失,所以就着一篇深度好文:知名互联网公司校招 Java 开发岗面试知识点解析 ,来好好的对 Java 知识点进行复习和学习一番,大 ...
- Java 面试知识点解析(六)——数据库篇
前言: 在遨游了一番 Java Web 的世界之后,发现了自己的一些缺失,所以就着一篇深度好文:知名互联网公司校招 Java 开发岗面试知识点解析 ,来好好的对 Java 知识点进行复习和学习一番,大 ...
- 深入解析HashMap、HashTable
集合类之番外篇:深入解析HashMap.HashTable Java集合类是个非常重要的知识点,HashMap.HashTable.ConcurrentHashMap等算是集合类中的重点,可谓“重中之 ...
- 《Java面试全解析》505道面试题详解
<Java面试全解析>是我在 GitChat 发布的一门电子书,全书总共有 15 万字和 505 道 Java 面试题解析,目前来说应该是最实用和最全的 Java 面试题解析了. 我本人是 ...
- 《Java面试全解析》1000道面试题大全详解(转)
<Java面试全解析>1000道 面试题大全详解 本人是 2009 年参加编程工作的,一路上在技术公司摸爬滚打,前几年一直在上海,待过的公司有 360 和游久游戏,因为自己家庭的原因,放弃 ...
- 深度解析HashMap底层实现架构
摘要:分析Map接口的详细使用以及HashMap的底层是如何实现的? 本文分享自华为云社区<[图文并茂]深度解析HashMap高频面试及底层实现结构![奔跑吧!JAVA]>,原文作者:灰小 ...
随机推荐
- 如何用OCR文字识别软件将PDF转换成Excel
最近老板老是让小编处理PDF文件,这OCR识别软件咱也不懂,也不敢问,只能一字一字的码在Excel上,但是这波操作效率不高,还没完成任务,老板又发了一堆PDF文件需要处理,怎么办呢? 跟朋友说了这事后 ...
- leetcode187. 重复的DNA序列
所有 DNA 都由一系列缩写为 A,C,G 和 T 的核苷酸组成,例如:"ACGAATTCCG".在研究 DNA 时,识别 DNA 中的重复序列有时会对研究非常有帮助.编写一个函数 ...
- leetcode116. 填充每个节点的下一个右侧指针
给定一个完美二叉树,其所有叶子节点都在同一层,每个父节点都有两个子节点.二叉树定义如下:struct Node { int val; Node *left; Node *right; Node ...
- wraps装饰器的使用
functools模块中的wraps装饰器 说明 使用functools模块提供的wraps装饰器可以避免被装饰的函数的特殊属性被更改,如函数名称__name__被更改.如果不使用该装饰器,则会导致函 ...
- Alpha冲刺-第三次冲刺笔记
Alpha冲刺-冲刺笔记 这个作业属于哪个课程 https://edu.cnblogs.com/campus/fzzcxy/2018SE2 这个作业要求在哪里 https://edu.cnblogs. ...
- 跟我一起学Redis之Redis持久化必知必会
前言 Redis是出了名的速度快,那是因为在内存中进行数据存储和操作:如果仅仅是在内存中进行数据存储,那就会导致以下问题: 数据随进程退出而消失:当服务器断电或Redis Server进程退出时,内存 ...
- 20190814_tomcat配置项目的错误页
1. 打开项目中的web.xml, 注意不是tomcat的web.xml; 一般是在项目的 WEB-INF目录下, 然后加上下面的语句 <error-page> <error-cod ...
- moviepy音视频剪辑:视频基类VideoClip子类VideoFileClip、CompositeVideoClip、ImageSequenceClip介绍
☞ ░ 前往老猿Python博文目录 ░ 一.引言 在<moviepy音视频剪辑:moviepy中的剪辑相关类及关系>介绍了VideoClip主要有六个直接子类(VideoFileClip ...
- 第8.11节 Python类中记录实例变量属性的特殊变量__dict__
一. 语法释义 调用方法:实例. __dict__属性 __dict__属性返回的是实例对象中当前已经定义的所有自定义实例变量的名和值,用字典存储,每个元素为一个"实例变量名:值" ...
- PyQt(Python+Qt)学习随笔:QScrollArea滚动区域layout布局的作用及设置方法
老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 滚动区域可以设置布局,布局的作用其实与其他布局一样,就是在滚动区域大小调整时,滚动区域内的子部件跟随 ...