小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
HashMap
哈希表(又称散列表)的原理为:借助 哈希函数,将键映射到存储桶地址。更确切地说,
-
首先开辟一定长度的,具有连续物理地址的桶数组;
-
当我们插入一个新的键时,哈希函数将决定该键应该分配到哪个桶中,并将该键存储在相应的桶中;
-
当我们想要搜索一个键时,哈希表将使用哈希函数来找到对应的桶,并在该桶中进行搜索。
哈希冲突的解决
链表+红黑树
负载因子
0.75
扩容机制
2倍扩容
在多线程下可能的问题
1、并发插入是可能会出现带环链表,使下一次读操作出现死循环。扩容时若程序死循环可能导致CPU飙升100%
由于hashMap本身的容量不大(初始值为16),当CPU多线程扩容的时候内部扩容的逻辑会反转散列桶中的节点顺序;当有多个线程同时进行扩容的时候,由于 HashMap 并非线程安全的,所以如果两个线程同时反转的话,便可能形成一个循环,并且这种循环是链表的循环,相当于 A 节点指向 B 节点,B 节点又指回到 A 节点,这样一来,在下一次想要获取该 key 所对应的 value 的时候,便会在遍历链表的时候发生永远无法遍历结束的情况,在程序死循环时候更会出现,也就发生 CPU 飙升100% 的情况。
2.若put碰撞可能引发数据丢失
在jdk8之后对hashMap进行了优化,hash碰撞不再采用头插法而采用尾插法插入新节点。若a,b线程put中key的hash值相同,当线程a获取m值后未插入,此时线程b也获取m值后进行插入操作。由于hashmap不同步,导致其中一个线程的value值会丢失,这是不允许的,这就发生了线程安全问题。
HashMap 和 HashTable 区别
- Hashtable 是不允许键或值为 null 的,HashMap 的键值则都可以为 null。
- 实现方式不同:Hashtable 继承了 Dictionary类,而 HashMap 继承的是 AbstractMap 类。
- 初始化容量不同:HashMap 的初始容量为:16,Hashtable 初始容量为:11,两者的负载因子默认都是:0.75。
- 扩容机制不同:当现有容量大于总容量 * 负载因子时,HashMap 扩容规则为当前容量翻倍,Hashtable 扩容规则为当前容量翻倍 + 1。
- 迭代器不同:HashMap 中的 Iterator 迭代器是 fail-fast 的,而 Hashtable 的 Enumerator 不是 fail-fast 的。