【面试官】HashMap为什么线程不安全?
- 面试官:你说下HashMap的内部结构?
- 面试官:那一个键值是怎么存储到HashMap的?
- 面试官:HashMap链表还会转换成什么?
- 面试官:HashMap为什么线程不安全?
- 面试官:有线程安全的Map吗?
- 面试官:HashTable和ConcurrentHashMap有什么区别吗?
- 👉以【面试官面试】形式覆盖Java程序员所需掌握的Java核心知识、面试重点
- 📚本期是《Java系列》,其他系列博客请订阅专栏《Java Offer训练营》
- ❤创作不易,不妨点赞、收藏、关注支持一下
文章目录
- 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的链表元素如果数量过多,查询效率会越来越低,所以需要将链表转换为其他数据结构。而二叉搜索树这种数据结构是绝对的子树平衡,左节点比父节点小,右节点比父节点大,在极端情况会退化为链表结构。
而红黑树放弃了绝对的子树平衡,转而追求的是一种大致平衡,在极端情况下的数据查询效率更优。
stat
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
👉以贴近现实的【面试官面试】形式帮助你系统学习后端技术 👉成体系知识帮你在后端进阶,每一道问答助你怒怼大厂面试官,收获大厂offer 👉《Java Offer训练营》包含Redis系列、MySQL系列、Kafka系列、ZooKeeper系列、JVM系列、多线程系列等等 👉制作不易,各位的支持是我创作的最大动力