HashMap30连问,彻底搞懂HashMap

文章目录

  • 一、背景知识
    • 1、什么是Map?
    • 2、什么是Hash?
    • 3、什么是哈希表?
    • 4、什么是HashMap?
    • 5、如何使用HashMap?
    • 6、HashMap有哪些核心参数?
    • 7、HashMap与HashTable的对比?
    • 8、HashMap和HashSet的区别?
    • 9、什么是LinkedHashMap和TreeMap?
  • 二、HashMap的实现原理
    • 10、HashMap的数据结构?
    • 11、HashMap put元素的原理?
    • 12、HashMap get元素的原理?
  • 三、红黑树
    • 13、为什么要将链表转化为红黑树?
    • 14、链表元素超过8转化为红黑树,那为什么不是红黑树元素小于等于8转化为链表,而是小于等于6?
    • 15、链表元素超过8是否一定转化为红黑树?
  • 三、hash计算和index计算
    • 16、HashMap如何计算K的hash值?
    • 17、为什么不用K的hashCode值直接作为hash值,而是将hashCode值进行无符号右移16位,再异或的复杂操作?
    • 18、HashMap如何计算K的数组下标?
  • 四、HashMap的扩容
    • 19、HashMap什么时候扩容?
    • 20、为什么HashMap的容量必须是2的幂?
    • 21、JDK7如何实现HashMap的扩容?
    • 22、1.7为什么采用头插法?
    • 23、头插法为什么会造成HashMap的死循环?
    • 24、JDK8做了哪些改进,避免死循环?
    • 25、JDK8扩容的优化点?
    • 26、HashMap线程安全吗?
    • 27、什么是ConcurrentHashMap?
    • 28、ConcurrentHashMap JDK7的实现原理?
    • 29、ConcurrentHashMap JDK8的实现原理?
    • 30、JDK8对HashMap做了哪些优化?

一、背景知识

面试过程中面试官的死亡连:你说你看过很多源码是吗?那你说说hashmap的底层实现?什么条件下会自动扩容的?为什么要有 红黑树?什么条件下会有?扩容因子为什么是0.75有研究过吗? 下来就来彻底了解一下HashMap吧!

1、什么是Map?

Map是一种集合接口,提供了一系列操作K-V的方法,HashMap、HashTable都是Map接口的实现类。

2、什么是Hash?

  • Hash 音译为 “哈希” ,也叫 “散列” 。

  • 哈希的本质是通过哈希函数对原始数据进行有损压缩,得到固定长度的输出,即哈希值,通过哈希值唯一标识原始数据。

  • 若不同的原始数据被有损压缩后产生了相同的结果,该现象称为哈希碰撞。

「」迎接HashMap 30连,彻底搞懂HashMap

3、什么是哈希表?

哈希表是一种数据结构,它可以提供较高的存取效率。

【原理】

1、向哈希表插入元素时,会先根据哈希函数计算K对应的存储位置,再put元素;

2、查询时,根据哈希函数计算K对应的存储位置,直接访存储位置获取元素,查询效率高。

「」迎接HashMap 30连,彻底搞懂HashMap

【基本概念】

4、什么是HashMap?

HashMap继承了AbstractMap类,是Map接口的一种实现,用于存储K-V数据结构的元素,底层通过哈希表实现了较快的存取效率。

5、如何使用HashMap?

Map map = new HashMap(); // 创建HashMap对象
map.put("数学", 91 ); // 存放元素
map.put("语文", 92 ); // 存放元素
map.put("物理", 94 ); // 存放元素
int score = map.get("语文"); // 获取元素值
for(Object key : map.keySet()) { // 遍历元素System.out.println("科目"key + "的成绩是" + map.get(key));
}
map.remove("物理"); // 删除元素

6、HashMap有哪些核心参数?

1、默认初始化容量

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // 16

2、最大容量

static final int MAXIMUM_CAPACITY = 1 << 30;

3、默认负载因子

static final float DEFAULT_LOAD_FACTOR = 0.75f;

4、树形化阈值

static final int TREEIFY_THRESHOLD = 8;

5、解树形化阈值

static final int UNTREEIFY_THRESHOLD = 6;

7、HashMap与HashTable的对比?

【相同点

都用于存储K-V元素

【不同点】

  1. HashMap可接受null键值和值,Hashtable则不能;
  2. HashMap线程不安全,HashTable线程安全,方法都加了synchronized;
  3. HashMap继承AbstractMap类,HashTable继承Dictionary类;
  4. HashMap的迭代器(Iterator)是fail-fast迭代器,HashTable的enumerator迭代器不是fail-fast,所以遍历时如果有线程改变了HashMap(增加或者移除元素),将会抛出
    ConcurrentModificationException。
  5. HashMap默认容量16,扩容为old2;HashTable默认容量11,扩容为old2+1。

8、HashMap和HashSet的区别?

HashSet实现Set接口,不允许集合中有重复值;HashMap实现Map接口,存储键值对,K不允许重复。

9、什么是LinkedHashMap和TreeMap?

  • LinkedHashMap内部维护一个链表,存储了K的插入顺序,迭代时按照插入顺序迭代。

  • TreeMap底层是一颗红黑树,containsKey、get、put、remove方法的时间复杂度都是
    log(n),按K的自然顺序排列(如整数的从小到大),也可指定Comparator比较函数。

二、HashMap的实现原理

10、HashMap的数据结构?

JDK1.7及之前,HashMap的内部数据结构是数组+链表。

「」迎接HashMap 30连,彻底搞懂HashMap

JDK1.8开始,当链表长度 > 8时会转化为红黑树,当红黑树元素个数 ≤ 6时会转化为链表。

11、HashMap put元素的原理?

「」迎接HashMap 30连,彻底搞懂HashMap

12、HashMap get元素的原理?

「」迎接HashMap 30连,彻底搞懂HashMap

三、红黑树

13、为什么要将链表转化为红黑树?

链表长度太长时,红黑树的存取效率比链表高。

14、链表元素超过8转化为红黑树,那为什么不是红黑树元素小于等于8转化为链表,而是小于等于6?

如果链表和红黑树互相转换的阈值固定是8,当HashMap元素个数在8左右变更时,

会导致树和链表数据结构的频繁变更,降低性能,所以中间预留buffer。

15、链表元素超过8是否一定转化为红黑树?

链表长度大于8 && 数组长度大于64,才会树形化,否则只是resize扩容。

为什么呢?因为数组小而链表长的场景,将链表转换为树治标不治本,应优先扩容数组。

三、hash计算和index计算

16、HashMap如何计算K的hash值?

hash值 = (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);

17、为什么不用K的hashCode值直接作为hash值,而是将hashCode值进行无符号右移16位,再异或的复杂操作?

Object的hashCode()函数返回的值是int型,值范围从-2147483648到2147483648,转化为2进制有32位,如、1011000110101110011111010011011。

使用右移、异或的操作,可以充分利用K的hashCode值高低位不同的特性,以减少hash碰撞的可能,提升查询效率。

18、HashMap如何计算K的数组下标?

index = h & (length-1)

四、HashMap的扩容

19、HashMap什么时候扩容?

  • 当元素数量超过阈值时扩容,阈值 = 数组容量 * 加载因子。
  • 数组容量默认16,加载因子默认0.75,所以默认阈值12。
  • 容量上限为1 << 30

20、为什么HashMap的容量必须是2的幂?

index = h & (length-1),2的幂次方-1都是1,可以充分利用高低位特点,减少hash冲突。

21、JDK7如何实现HashMap的扩容?

源码、

void resize(int newCapacity) {Entry[] oldTable = table;int oldCapacity = oldTable.length;if (oldCapacity == MAXIMUM_CAPACITY) {threshold = Integer.MAX_VALUE;return;}Entry[] newTable = new Entry[newCapacity];transfer(newTable, initHashSeedAsNeeded(newCapacity));table = newTable;threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
}void transfer(Entry[] newTable, boolean rehash) {int newCapacity = newTable.length;for (Entry<K,V> e : table) {while(null != e) {Entry<K,V> next = e.next;if (rehash) {e.hash = null == e.key ? 0 : hash(e.key);}int i = indexFor(e.hash, newCapacity);e.next = newTable[i];newTable[i] = e;e = next;}}
}

不想看源码 ?直接看我整理的简单流程图

「」迎接HashMap 30连,彻底搞懂HashMap

22、1.7为什么采用头插法?

刚添加的元素,被访的概率大。

23、头插法为什么会造成HashMap的死循环?

【step1】现有HashMap,table大小为2,里面有3个元素在index1处。

「」迎接HashMap 30连,彻底搞懂HashMap

【step2】有2个线程同时触发了扩容,但线程2刚启动扩容就被挂起,此时线程2内e指向了key(A),其next指向了key(B),而线程1完成了扩容。

「」迎接HashMap 30连,彻底搞懂HashMap

【step3】线程2被调度回来,线程2内当前待调整元素e指向A,所以头插A。此时e.next=B,待调整B。

「」迎接HashMap 30连,彻底搞懂HashMap

【step4】e = next指向B,所以头插B,此时e.next = table[i] ,本来应该指向C,但由于线程1已经完成了扩容,所以又指向了A。

「」迎接HashMap 30连,彻底搞懂HashMap

【step5】头插A,形成环。

「」迎接HashMap 30连,彻底搞懂HashMap

24、JDK8做了哪些改进,避免死循环?

1、链表太长转换为红黑树,减少死循环发生的可能;

2、1.8使用尾插法,在扩容时会保持链表元素原来的顺序,解决了死循环题,但解决不了线程不安全题。

25、JDK8扩容的优化点?

1、头插法改为尾插法,解决链表死循环题。

2、扩容的效率更高

扩容前index = hash&(oldTable.length-1),
扩容后index = hash&(oldTable.length*2-1)

唯一的区别是 length -1,多了一个高位1参与运算,如果hash对应的位置是0,则Node的index没变,如果hash对应位置是1,则newIndex =oldIndex + oldLength。

即得出结论、扩容后,Node的位置要么不变,要么移动odLength。

因此,在扩容时,不需要重新计算元素的hash了,只需要通过 if ((e.hash & oldCap) == 0)
判断最高位是1还是0就可以确定index,效率更高。

【线程安全】

26、HashMap线程安全吗?

HashMap非线程安全,线程安全的Map可以使用ConcurrentHashmap。

27、什么是ConcurrentHashMap?

HashMap线程不安全,多线程环境可以使用Collections.synchronizedMap、HashTable实现线程安全,但性能不佳。

ConurrentHashMap比较适合高并发场景使用。

28、ConcurrentHashMap JDK7的实现原理?

1、数据结构、Segment数组+HashEntry链表数组

ConcurrentHashMap由一个Segment数组构成(默认长度16),Segment继承自ReentrantLock,所以加锁时Segment数组元素之间相互不影响,所以可实现分段加锁,性能高。

Segment本身是一个HashEntry链表数组,所以每个Segment相当于是一个HashMap

「」迎接HashMap 30连,彻底搞懂HashMap

2、put元素原理

「」迎接HashMap 30连,彻底搞懂HashMap

3、get元素原理

「」迎接HashMap 30连,彻底搞懂HashMap

get()操作不需要加锁,是因为HashEntry的元素val和指针next都使用volatile修饰,在多线程环境下,线程A修改Node的val或新增节点时,对线程B都是可见的。

29、ConcurrentHashMap JDK8的实现原理?

1、数据结构、Node数组+链表/红黑树,类似1.8的HashMap。

摒弃Segment,使用Node数组+链表+红黑树的数据结构。

桶中的结构可能是链表,也可能是红黑树,红黑树是为了提高查找效率。

并发控制使用Synchronized和CAS来操作,整体看起来像是线程安全的JDK8 HashMap。

30连,彻底搞懂HashMap

2、存放元素原理

「」迎接HashMap 30连,彻底搞懂HashMap

3、获取元素原理

计算hash - 计算数组下标 - 遍历节点

30、JDK8对HashMap做了哪些优化?

1、引入红黑树 ,提升元素获取速度。

2、头插法改为尾插法,解决链表死循环题。

3、扩容的效率更高

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/450822.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何使用postman做接口测试

1、get请求传参 只要是get请求都可以在浏览器中直接发&#xff1a; 在访问地址后面拼 ?keyvalue&keyvalue 例如&#xff1a;在浏览器中直接输入访问地址&#xff0c;后面直接拼需要传给服务器的参数http://api.nnzhp.cn/api/user/stu_info?stu_name小黑2、post请求&…

【狂神说】分析前后端分离开源项目?

文章目录1.如何分析开源项目项目简介项目源码2.观察开源项目3.开源项目下载4.跑起来是第一步5.前后端分离项目固定套路6.如何找到一个开源项目1.如何分析开源项目 学习的方式&#xff1a; 不知道这个代码怎么来的这个代码跑不起来这个项目对我们有什么帮助&#xff0c;不会模…

设计公共API的六个注意事项

摘要&#xff1a;俗话说&#xff1a;“好东西就要贡献出来和大家一起分享”&#xff0c;尤其是在互联网业务高度发达的今天&#xff0c;如果你的创业公司提供了一项很酷的技术或者服务&#xff0c;并且其他用户也非常喜欢该产品&#xff0c;在这种情况下&#xff0c;最好的解决…

go 交叉编译

golang中windows交叉编译 env GOOSlinux GOARCHamd64 go build .打包镜像 FROM alpineMAINTAINER "congge"ADD ./casino_niuniu /usr/local/casino_niuniu/bin/casino_niuniu ADD ./templates /usr/loca/lcasino_niuniu/bin/templates ADD ./public /usr/local/casin…

P3193 [HNOI2008]GT考试

传送门 容易看出是道DP 考虑一位一位填数字 设 f [ i ] [ j ] 表示填到第 i 位&#xff0c;在不吉利串上匹配到第 j 位时不出现不吉利数字的方案数 设 g [ i ] [ j ] 表示不吉利串匹配到第 i 位&#xff0c;再添加一个数字&#xff0c;使串匹配到第 j 位的方案数 那么方程显然为…

SQLserver数据库反编译生成Hibernate实体类和映射文件

一、建立项目和sqlserver数据库 eclipse&#xff0c;我使用的版本是neon3 二、Data Source Explorer 选择OK 在data source Explorer的Database Connections 选择New 填写好General的连接信息 新建New Driver Definition 填写完选择OK 选择刚才的Drivers Test Connetion测试 N…

最受欢迎的5大Linux发行版

摘要&#xff1a;要统计有多少人在使用那款Linux发行版几乎是不可能的事情&#xff0c;但我们可以使用一些在线分析工具来大概地看看哪些Linux发行版更受欢迎。 Google Trends的数据显示&#xff0c;Ubuntu用户正在流向Mint&#xff0c;但依然在各方面都比其它Linux发行版更有优…

使用IntelliJ IDEA 配置Maven(入门)

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 1. 下载Maven 官方地址&#xff1a;http://maven.apache.org/download.cgi 解压并新建一个本地仓库文件夹 2.配置本地仓库路径 3.配…

为什么程序员不擅长估算时间?

摘要&#xff1a;时间估算是困难的&#xff0c;每一个程序员都有一个现实的估计区间&#xff0c;低于这个区间的估计意味着&#xff08;构件&#xff0c;测试&#xff0c;检查代码的&#xff09;时间开销被低估了&#xff0c;超过这个区间的估计意味着这个任务太大而很难预估。…

red hat enterprise linux 7关闭防火墙的方法

2019独角兽企业重金招聘Python工程师标准>>> red hat enterprise linux 7发布后&#xff0c;发现防火墙也变了&#xff0c;如何关闭防火墙呢&#xff0c;下面是方法 1.查看firewall的状态 [rootsztech7 ~]# systemctl status firewalld firewalld.service - firewal…

IOS —— 网络那些事(上) - http协议

作为一名并不太合格的程序员&#xff0c;今天要分享学习的成果&#xff0c;竟然讲的是网络相关HTTP协议的事情。&#xff08;也算是复习了&#xff09; 乍看HTTP协议的内容着实是十分复杂的&#xff0c;涉及到十分多互联网"底层"框架的东西。今天就先撇开这部分详细内…

老派程序员——徒手实现伟大成就

摘要&#xff1a;本文介绍了三位非常著名的程序员&#xff1a;Ken Thompson,Joe Armstrong 和 Jamie Zawinski&#xff0c;他们是如何发明一门新语言&#xff0c;他们开发软件时会像我们一样使用当今流行的开发工具吗&#xff1f;当读Peter Seibel的精彩著作《编程人生:15位软件…

互联网大厂项目研发流程

文章目录阶段一&#xff1a;阶段二&#xff1a;阶段三&#xff1a;阶段四&#xff1a;阶段五&#xff1a;开发人员&#xff1a;测试人员&#xff1a;设计师&#xff1a;阶段六&#xff1a;阶段七&#xff1a;总结&#xff1a;本文章学习自&#xff1a;https://www.bilibili.com…

解决 java.lang.IllegalArgumentException: Repository interface must not be null on initialization!

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 报错&#xff1a;Caused by: java.lang.IllegalArgumentException: Repository interface must not be null on initialization! Cause…

【狂神说】JVM

文章目录1.JVM的位置2.JVM的体系结构3.类加载器4.双亲委派机制&#xff08;重要&#xff09;5.沙箱安全机制(了解)6.native&#xff08;核心&#xff09;7.PC寄存器&#xff08;了解&#xff09;8.方法区9.栈10.三种JVM11.堆&#xff08;Heap&#xff09;12.新生区、老年区13.永…

我们真的需要统一的编程规范?

摘要&#xff1a;仁者见仁智者见智&#xff0c;编码风格的不同&#xff0c;对项目也会有不同的影响&#xff0c;统一的编码规范有益于项目的维护。俗话说&#xff0c;没有规矩不成方圆&#xff0c;在2004年&#xff0c;UNIX创始人之一的Ken Arnold就发表了一篇很幽默文章&#…

百度云重磅发布ABC 3.0 尹世明如何诠释百度云的“新”打法

雷锋网9月4日消息&#xff0c;2018百度云智峰会正式召开&#xff0c;百度总裁张亚勤发表题为《新技术驱动&#xff0c;全面进入Cloud2.0》的演讲并表示&#xff0c;经历了PCClient/Server到MobileCloud 1.0&#xff0c;再到如今的AICloud 2.0过程&#xff0c;新技术推动云计算产…

开发人员眼中最好的代码编辑器是谁?

摘要&#xff1a;对开发人员来讲&#xff0c;开发工具就好比战场上的“兵器”&#xff0c;不同领域的开发人员他们所使用的“兵器”也不完全相同&#xff0c;本文从友好性、功能性、扩展等多方面总结了最受开发人员欢迎的“兵器”。你最爱的那个在这里吗&#xff1f; 如果我们把…

【老杜】MySQL—day01

文章目录day01课堂笔记1、数据库概述及数据准备1.1、什么是数据库1.2、什么是数据库管理系统1.3、SQL概述1.4、安装MySQL数据库管理系统。1.4、MySQL数据库的完美卸载&#xff01;1.5、MySQL的服务1.6、用命令来启动和关闭mysql服务1.7、登录mysql数据库2、MySQL常用命令&#…

【转载】DRuid 大数据分析之查询

转载自http://yangyangmyself.iteye.com/blog/23217591、Druid 查询概述上一节完成数据导入后&#xff0c;接下来讲讲Druid如何查询及统计分析导入的数据。Druid的查询是使用REST风格的HTTP请求查询服务节点&#xff08;Broker、Historical、Realtime&#xff09;&#xff0c;这…