------------恢复内容开始------------

在介绍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的更多相关文章

  1. 知名互联网公司校招 Java 开发岗面试知识点解析

    天之道,损有余而补不足,是故虚胜实,不足胜有余. 本文作者在一年之内参加过多场面试,应聘岗位均为 Java 开发方向.在不断的面试中,分类总结了 Java 开发岗位面试中的一些知识点. 主要包括以下几 ...

  2. Java 面试知识点解析(四)——版本特性篇

    前言: 在遨游了一番 Java Web 的世界之后,发现了自己的一些缺失,所以就着一篇深度好文:知名互联网公司校招 Java 开发岗面试知识点解析 ,来好好的对 Java 知识点进行复习和学习一番,大 ...

  3. Java 面试知识点解析(一)——基础知识篇

    前言: 在遨游了一番 Java Web 的世界之后,发现了自己的一些缺失,所以就着一篇深度好文:知名互联网公司校招 Java 开发岗面试知识点解析 ,来好好的对 Java 知识点进行复习和学习一番,大 ...

  4. Java 面试知识点解析(二)——高并发编程篇

    前言: 在遨游了一番 Java Web 的世界之后,发现了自己的一些缺失,所以就着一篇深度好文:知名互联网公司校招 Java 开发岗面试知识点解析 ,来好好的对 Java 知识点进行复习和学习一番,大 ...

  5. Java 面试知识点解析(六)——数据库篇

    前言: 在遨游了一番 Java Web 的世界之后,发现了自己的一些缺失,所以就着一篇深度好文:知名互联网公司校招 Java 开发岗面试知识点解析 ,来好好的对 Java 知识点进行复习和学习一番,大 ...

  6. 深入解析HashMap、HashTable

    集合类之番外篇:深入解析HashMap.HashTable Java集合类是个非常重要的知识点,HashMap.HashTable.ConcurrentHashMap等算是集合类中的重点,可谓“重中之 ...

  7. 《Java面试全解析》505道面试题详解

    <Java面试全解析>是我在 GitChat 发布的一门电子书,全书总共有 15 万字和 505 道 Java 面试题解析,目前来说应该是最实用和最全的 Java 面试题解析了. 我本人是 ...

  8. 《Java面试全解析》1000道面试题大全详解(转)

    <Java面试全解析>1000道 面试题大全详解 本人是 2009 年参加编程工作的,一路上在技术公司摸爬滚打,前几年一直在上海,待过的公司有 360 和游久游戏,因为自己家庭的原因,放弃 ...

  9. 深度解析HashMap底层实现架构

    摘要:分析Map接口的详细使用以及HashMap的底层是如何实现的? 本文分享自华为云社区<[图文并茂]深度解析HashMap高频面试及底层实现结构![奔跑吧!JAVA]>,原文作者:灰小 ...

随机推荐

  1. 浅谈 倍增/ST表

    命题描述 给定一个长度为 \(n\) 的序列,\(m\) 次询问区间最大值 分析 上面的问题肯定可以暴力对吧. 但暴力肯定不是最优对吧,所以我们直接就不考虑了... 于是引入:倍增 首先,倍增是个什么 ...

  2. 牛客练习赛69 E 字串(哈希)

    题目链接 题目大意 给出一个长度为n排列p 规定一个区间 [l,r] (l<=r) 是 fair 的,当且仅当区间中最小值等于 l 并且最大值等于 r 求 fair 区间的个数 题目思路 我不会 ...

  3. tar解压提示:tar (child): 无法连接至 xxxx: 解析失败

    如图提示: 错误原因:由于压缩文件中含有冒号导致 解决办法: 使用tar命令的–force-local选项

  4. mysql hash join

    面阿里问到的. "mysql join的实现有几种方式?" "啥?不就一种吗?" 转载: 简单介绍:https://zhuanlan.zhihu.com/p/9 ...

  5. 17.java设计模式之观察者模式

    基本需求: 气象站可以将每天测量到的温度,湿度,气压等等,以公告的形式发布出去(比如发布到自己的网站或第三方) 需要设计开放型API,便于其他第三方也能接入气象站获取数据 提供温度.气压和湿度的接口 ...

  6. Java进阶专题(十八) 系统缓存架构设计 (下)

    前言 上章节介绍了Redis相关知识,了解了Redis的高可用,高性能的原因.很多人认为提到缓存,就局限于Redis,其实缓存的应用不仅仅在于Redis的使用,比如还有Nginx缓存,缓存队列等等.这 ...

  7. charles功能(二)修改response请求参数

    1.接口处 鼠标右击,选择breakpoints(允许本接口使用breakpionts功能) 2.开始设置断点值 4.重新请求接口(charles的界面变为可编辑状态),修改请求参数,执行请求 5.最 ...

  8. 基于gRPC的注册发现与负载均衡的原理和实战

    gRPC是一个现代的.高性能.开源的和语言无关的通用RPC框架,基于HTTP2协议设计,序列化使用PB(Protocol Buffer),PB是一种语言无关的高性能序列化框架,基于HTTP2+PB保证 ...

  9. Python正则表达式re模块和os模块实现文件搜索模式匹配

    ☞ ░ 前往老猿Python博文目录 ░ 因测试需要,需要提供一个可以指定目录搜索符合条件的文件名函数,搜索时可以通过*(星号)匹配0-n个字符,?(问号)匹配任意1个字符,可以指定多个文件类型,每个 ...

  10. Hive 表操作(HIVE的数据存储、数据库、表、分区、分桶)

    1.Hive的数据存储 Hive的数据存储基于Hadoop HDFS Hive没有专门的数据存储格式 存储结构主要包括:数据库.文件.表.试图 Hive默认可以直接加载文本文件(TextFile),还 ...