文章目录

  • 一、背景知识
    • 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 音译为 “哈希” ,也叫 “散列” 。

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

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

3、什么是哈希表?

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

【原理】

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

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

【基本概念】

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的内部数据结构是数组+链表。

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

11、HashMap put元素的原理?

12、HashMap get元素的原理?

三、红黑树

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

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

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

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

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

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

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

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

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

【step5】头插A,形成环。

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

2、put元素原理

3、get元素原理

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

29、ConcurrentHashMap JDK8的实现原理?

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

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

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

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

2、存放元素原理

3、获取元素原理

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

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

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

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

3、扩容的效率更高

HashMap30连问,彻底搞懂HashMap相关推荐

  1. hashmap 允许key重复吗_搞懂 HashMap,这一篇就够了

    HashMap 概述 「如果你没有时间细抠本文,可以直接看 HashMap 概述,能让你对 HashMap 有个大致的了解」. HashMap 是 Map 接口的实现,HashMap 允许空的 key ...

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

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

  3. 通过一个实际案例,彻底搞懂 HashMap!

    我知道大家都很熟悉hashmap,并且有事没事都会new一个,但是hashmap的一些特性大家都是看了忘,忘了再记,今天这个例子可以帮助大家很好的记住. 场景 用户提交一张试卷答案到服务端,post报 ...

  4. HashMap源码分析(搞懂HashMap看这个就够了)

    首先来看看HashMap,从构造函数看起 HashMap有四个构造函数 第一个: public HashMap() {this.loadFactor = DEFAULT_LOAD_FACTOR; // ...

  5. 终于搞懂HashMap的源码了!!!

    背景 HashMap是我们在平时开发最常用的容器之一,但是我们有真正了解过他吗?他是线程安全的吗?他是以何种方式来存储的呢?为什么初始化的容器大小时2的n次幂呢?他是如何进行扩容的呢?他是如何实现并发 ...

  6. 100个问题搞懂Java并发

    写在前面 100个问题搞定Java虚拟机 100个问题搞定大数据理论体系 1000个问题搞定大数据技术体系 目录结构 什么叫线程安全? 同步和异步有什么区别? 并发和并行有什么区别? 死锁.活锁和饥饿 ...

  7. 搞懂 Java HashMap 源码

    HashMap 源码分析 前几篇分析了 ArrayList , LinkedList ,Vector ,Stack List 集合的源码,Java 容器除了包含 List 集合外还包含着 Set 和 ...

  8. 转:前端 100 问:能搞懂80%的请把简历给我

    <前端 100 问:能搞懂80%的请把简历给我> 引言 半年时间,几千人参与,精选大厂前端面试高频 100 题,这就是「壹题」. 在 2019 年 1 月 21 日这天,「壹题」项目正式开 ...

  9. 面试官问你斐波那契数列的时候不要高兴得太早 搞懂C语言函数指针 搜索引擎还可以这么玩? 那些相见恨晚的搜索技巧...

    面试官问你斐波那契数列的时候不要高兴得太早 前言 假如面试官让你编写求斐波那契数列的代码时,是不是心中暗喜?不就是递归么,早就会了.如果真这么想,那就危险了. 递归求斐波那契数列 递归,在数学与计算机 ...

最新文章

  1. 接口有个电池标志_USB接口上的小标签有啥用?从“+”号到闪电的奥秘
  2. 国产半导体路在何方,瓦森纳与光刻机你了解多少
  3. Eclipse运行jsp动态网页时,提示服务端口在使用中
  4. 2020 我的C++的学习之路
  5. 云信“欢乐颂活动”中奖名单
  6. 如何利用《C++ Primer》学习C++?
  7. html自定义鼠标右键,js自定义鼠标右键的实现原理及源码
  8. Delphi IDE使用的一些主要技巧
  9. mysql 日期间隔_mysql比较两个日期间隔
  10. OpenCart 之 CSV 格式商品导入 – 如何导入商品主图片和附加图片?
  11. codeforces 258div2 B Sort the Array
  12. 性能优化篇 - Performance(工具 api)
  13. 小米小钢炮等常用蓝牙设备(音箱/键盘/打印机)连接电脑(Windows/Linux)使用笔记
  14. flash html 通信,Javascript与flash交互通信基础教程
  15. jeesit的使用(一)
  16. Python反编译apk,获取各类信息
  17. 怎么用计算机表示素数,在线质数(素数)计算器
  18. SQL server2019导入Access 2016数据失败解决方法
  19. 计算机维护系统管理毕业设计,计算机系统维护-毕业论文.doc
  20. QQ音乐、网易云音乐、酷狗音乐歌单导入到Spotify

热门文章

  1. 我的世界一个程序导致JAVA,Java地位无可动摇的12个原因
  2. 规范化流程化提交自己代码到远程gitlab服务器
  3. 中科大 计算机网络4 网络核心Core 分组交换 电路交换
  4. 英语口语 Week15 TuesDay
  5. Linphone编译【转载】
  6. 认识高清视频编码(MPEG、H.264、WMV-HD、RMVB)
  7. 拜托!面试请不要再问我 Spring Cloud 底层原理 ...
  8. java中ftp文件上传和中文乱码解决
  9. [C# 网络编程系列]专题十二:实现一个简单的FTP服务器
  10. 2019-03-28 SQL Server Pivot