【面试官】HashMap为什么线程不安全?
- 面试官:你说下HashMap的内部结构?
- 面试官:那一个键值是怎么存储到HashMap的?
- 面试官:HashMap链表还会转换成什么?
- 面试官:HashMap为什么线程不安全?
- 面试官:有线程安全的Map吗?
- 面试官:HashTable和ConcurrentHashMap有什么区别吗?
大家好,我是南哥。
一个Java学习与进阶的领路人,跟着南哥我们一起Java成长。
文章目录
- HashMap内部结构
- 键值的添加流程
- 红黑树
- 线程安全的Map
- 线程不安全的HashMap
- 线程安全的ConcurrentHashMap
- HashTable和ConcurrentHashMap
1. HashMap内部结构
面试官:你说下HashMap的内部结构?
好的面试官。
HashMap内部存储数据的对象是一个实现Entry接口的Node数组,也称为哈希桶transient Node<K,V>[] table
,后面我们称Node数组为Entry数组。Entry数组初始的大小是16。
Node节点的内部属性key、value分别代表键和值,hash代表key的hash值,而next则是指向下一个链表节点的指针。
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
}
1.1 键值的添加流程
面试官:那一个键值是怎么存储到HashMap的?
首先会调用hash方法计算key的hash值,通过key的hashCode值与key的hashCode高16位进行异或运算,使hash值更加随机与均匀。
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
再通过该hash值与Entry数组的长度相与,得到要存储到的索引位置int index = (table.length - 1) & hash
。如果该索引位置是空的,会把键值直接添加到表头,如果哈希冲突了则会用链表法形成一条链表。
数据添加后,会判断当前容量是否到达了threshold阈值,threshold等于负载因子loadFactor * table.length
。负载因子默认是0.75,threshold第一次扩容时为0.75 * 16 = 12。
如果到达阈值了则会对Entry数组进行扩容,扩容成为原来两倍容量的Entry数组。
1.2 红黑树
面试官:HashMap链表还会转换成什么?
当链表长度 >= 8时,会把链表转换为红黑树。
是这样的,HashMap的链表元素如果数量过多,查询效率会越来越低,所以需要将链表转换为其他数据结构。而二叉搜索树这种数据结构是绝对的子树平衡,左节点比父节点小,右节点比父节点大,在极端情况会退化为链表结构。
而红黑树放弃了绝对的子树平衡,转而追求的是一种大致平衡,在极端情况下的数据查询效率更优。
static final class TreeNode<K,V> exten
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
👉以面试官面试的形式,涵盖了你怒怼大厂面试官、拿下大厂面试所需掌握的核心知识、面试重点! 👉相信一定对你顺利通关面试、拿到理想Offer有所帮助! 👉花费大量精力去制作本专栏,创作不易,各位的支持就是我创作的最大动力!