1.HashMap 数据结构
- JDK1.7:数组+链表
- 数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的。
- JDK1.8:数组+链表+红黑树
- 当链表过长,则会严重影响 HashMap 的性能,红黑树搜索时间复杂度是 O(logn),而链表是糟糕的 O(n)。
- 当链表超过 8 且数据总量超过 64 才会转红黑树。
- 将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树,以减少搜索时间。

- 数组:采用一段连续的存储单元来存储数据。
- 特点:查询 O(1) ,删除插入 O(N)
- 总结:查询快,删除插入慢
- 链表:链表是一种物理存储单元上非连续,非顺序的存储结构。
- 特点:插入、删除时间复杂度 O(1) ,查询遍历时间复杂度 O(N)
- 总结:插入快、查找慢
2.Hash 算法
哈希算法(也叫散列),就是把任意长度值(Key)通过散列算法变换成固定长度的 Key (地址) 通过这个地址进行访问的数据结构。
它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。
- Hashcode(取模/位运算)
- 通过字符串算出它的 ascii 码,进行 mod(取模),算出哈希表中的下标。
- 优点:能够节省数组的空间
- 缺点:会产生哈希冲突问题
- 链表出现原因:解决hash 碰撞。
3.JDK8 中的 HashMap 与 JDK7 的 HashMap 有什么不一样?
- JDK8 中新增了红黑树,JDK8 是通过 【数组+链表+红黑树】来实现的。
- JDK7 中链表的插入式用的头插法,而 JDK8 中则改为了尾插法。
- JDK8 中的因为使用了红黑树保证了插入和查询的效率,所以实际上 JDK8 中的 Hash 算法实现的复杂度降低了。
- JDK8 中数组扩容的条件也发生了变化,只会判断是否当前元素是否查过了阈值,而不再判断当前 put 进来的元素对应的数组下标位置是否有值。
- JDK7 中是先扩容再添加新元素,JDK8 中是先添加新元素然后再扩容。
4.HashMap 中 PUT 方法的流程
- 通过 Key 计算出一个 hashcode。
- 通过 hashcode 与“与操作”计算出一个数组下标
- 在把 put 进来的 key-value 封装为一个 entry 对象
- 判断数组下标对应的位置,是不是空,如果是空则把 entry 直接存在该数组位置
- 如果该下标对应的位置不为空,则需要把 entry 插入到链表中
- 并且还需要判断该链表中是否存在相同的 key ,如果存在,则更新 value
- 如果是 jdk7 ,则使用头插法
- 如果是 jdk8 ,则会遍历链表,并且在遍历链表的过程中,统计当前链表的元素个数,如果超过8个,则先把链表转变为红黑树,并且把元素插入到红黑树中
5.JDK8 中链表转变为红黑树的条件?
- 链表中的元素的个数为8个或超过8个
- 同时,还要满足当前数组的长度大于或等于64才会把链表转变为红黑树。
- 为什么?——>因为链表转变为红黑树的目的是为了解决链表过长,导致查询和插入效率慢的问题,而如果要解决这个问题,也可以通过数组扩容,把链表缩短也可以解决这个问题。所以在数组长度还不太长的情况,可以先通过数组扩容来解决链表过长的问题。

6.HashMap是线程不安全的,其主要体现
- 在jdk1.7中,在多线程环境下,扩容时会造成环形链或数据丢失。
- 在jdk1.8中,在多线程环境下,会发生数据覆盖的情况。
HashMap不是线程安全的,如果想要线程安全的HashMap,可以通过Collections类的静态方法synchronizedMap获得线程安全的HashMap。
Map map = Collections.synchronizedMap(new HashMap());https://mp.weixin.qq.com/s/UdonpQAYbTqFvxZR1Cm5lg
7.HashMap 解决hash冲突
HashMap中主要是通过key的hashCode来计算hash值的,只要hashCode相同,计算出来的hash值就一样。如果存储的对象对多了,就有可能不同的对象所算出来的hash值是相同的,这就出现了所谓的hash冲突。
【HashMap底层是通过链表来解决hash冲突的。】
8.HashMap 关键属性
- loadFactor加载因子是表示Hsah表中元素的填满的程度=0.75
- threshold临界值当实际大小超过临界值时,会进行扩容
- threshold = 加载因子*容量
9.HashMap 数组长度永远为 2 的幂次方
HashMap 是通过一个名为 tableSizeFor 的方法来确保 HashMap 数组长度永远为2的幂次方的。
tableSizeFor 的功能(不考虑大于最大容量的情况)是返回大于等于输入参数且最近的 2 的整数次幂的数。比如 10,则返回 16。
10.HashMap 为什么会导致 CPU 运行 100% ?(为什么会导致死循环?)

JAVA HASHMAP的死循环:https://coolshell.cn/articles/9606.html
参考资料:
- HashMap 源码详细分析(JDK1.8)
- hashCode() 和 equals() 之间的关系?
- 如何决定使用 HashMap 还是 TreeMap?
- 什么情况用ArrayList or LinkedList呢?
- HashMap怎样解决hash冲突
- HashMap的实现原理
- Java集合框架 10 连问
- Java容器面试题:谈谈你对 HashMap 的理解
- 谈谈ConcurrentHashMap是如何保证线程安全https://mp.weixin.qq.com/s/TnOVY0CluMAmUJE0MxA7Ig
- List如何一边遍历,一边删除?
- Java集合面试问题
- HashMap 中的容量与扩容实现
- 看完这篇,再也不怕面试被问HashMap
- ArrayList源码分析&手写ArrayList
- LinkedList源码分析
- HashMap源码分析
- TreeMap 原理实现及常用方法
- java集合中HashSet的原理及常用方
- 常用的Arraylist和Linkedlist的区别
- HashMap 面试二十一问
- hashCode()和equals()方法及使用规范
- Java线程安全与不安全的理解
- 链表高频面试题(包括反转、合并、相交、分割、环长等)https://mp.weixin.qq.com/s/tfHv11RNwjHDlqAsjGeD9w
- Java遍历Map集合有哪几种方式?各自效率怎么样?https://mp.weixin.qq.com/s/P976Tzvm06R_9BYpa29SqQ
版权声明:本文为cangsheng45原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。