???哪些实现方式?::直接定址法、数字分析法、除留余数法、分段叠加法、平方取中法、伪随机数法
**->:**HashMap在get(Object key)时,
(1)先高低位异或重新计算获得hashcode值,然后与运算取模计算得到在数组中的存储下标index
(2)如果是bucket里的第一个节点,则直接命中;否则有冲突,通过key.equals(k)查找对应的Node,若为树则O(logn),若为链表则 O(n);
**->:**碰撞探测,也即上述set和get方法中提到的hashCode相同时,则数组index位置处的bucket中以链表形式进行存储和查找
描述:HashMap初始化时,系统会创建一个长度为capcity(16)的的Node数组,该Node数组中可以存储node元素的位置被称为桶bucket(每个桶均有其索引,且只存储一个node元素,且node对象含有一个引用变量,用以指向下一个node进而构成node链表结构)
**->:**非线程安全,这是由于:
(1)不同线程在put操作时,如果是其中一个线程改变了某一键值对的value,那么其他的线程就get不到预期的值,而且值是在不停改变的,因此这样也不是线程安全的;
(2)在多线程中,当有两个线程同时在put操作的时候,如果存在扩容调用resize,那么也就是resize被两个线程同时调用,可能会产生环形链表,然后再执行get操作就会触发死循环引起CPU的100%问题
(3)fail-fast,如果在使用迭代器的过程中有其他线程修改了map的结构,则将抛出ConcurrentModificationException异常,也即所谓的fail-fast策略,此时开发人员应该注意线程安全问题
(说明)(a)避免多线程写时使用HashMap,单写多读是没有问题的;(b)或者为什么要在多线程中使用HashMap,Sun官方都不认为HashMap在多线程中的这个问题是个bug,因为HashMap本来就是不支持多线程使用的,如果需要并发的话就用ConcurrentHashMap;©或者通过方法Collections.synchroziedMap(Map),将HashMap转化为线程同步的Map
注意事项:
(1)使用String、Integer等引用类型的封箱wrapper类作为键Key使用;String最为常用,不可变且final的,已经重写的equals和hashCode方法 -> 当对象插入到HashMap之后,且Key就不会再改变了
(2)equals相等,必然hashCode相等 <=> hashCode不相等,必然equals不相等;
(3)hashCode相等,equals可能不相等;
(4)Hashtable使用了线程同步锁保护,也即Hashtable是粗暴地添加了同步锁(synchronized),导致性能会有损失 -> 更好的方式是使用ConcurrentHashMap,该类采用的是一种分段锁的机制实现线程安全的,后续将在单独一个篇幅中进行介绍!
【问4】??HashMap怎样解决冲突,讲一下扩容过程,假如一个值在原数组中,现在移动了数组,位置肯定改变了,那是什么定位到在这个新数组中的位置?
答:将新节点加到链表后,容量扩充为原来的两倍,然后对每个节点重新计算hash值,这个值只会出现在两个地方,一个是原下标的位置,另一种是在下标为<原下标+原容量>的位置
【问5】??抛开HashMap,hash冲突有哪些解决办法?
答:开放定址法,链地址法,再哈希法、建立公共溢出区
【问6】??针对HashMap中某个Node链太长,查找的时间复杂度可能达到O(n),怎么优化?
答:将链表转为红黑树,JDK1.8已经实现,时间复杂度变为O(logn)
三、Hashtable、HashMap、ConcurrentHashMap
ConcurrentHashMap使用分段锁技术,允许多个修改操作并发进行。ConcurrentHashMap内部使用段来表示这些不同的部分,每个段其实就是一个小的Hashtable,它们有自己的锁。只要多个修改操作发生在不同的段上,它们就可以并发进行。
Hashtable是一个线程安全的类,它使用synchronized来锁住整张Hash表来实现线程安全,即每次锁住整张表让线程独占
有些方法需要跨段,比如size()和containsValue(),它们可能需要锁定整个表而而不仅仅是某个段,这需要按顺序锁定所有段,操作完毕后,又按顺序释放所有段的锁。这里“按顺序”是很重要的,否则极有可能出现死锁,在ConcurrentHashMap内部,段数组是final的,并且其成员变量实际上也是final的,但是,仅仅是将数组声明为final的并不保证数组成员也是final的,这需要实现上的保证。这可以确保不会出现死锁,因为获得锁的顺序是固定的。
HashMap类:
-
Node
:链表节点,包含了key、value、hash、next指针四个元素 -
table: 《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》无偿开源 威信搜索公众号【编程进阶路】 Node
类型的数组,里面的元素是链表,用于存放HashMap元素的实体 -
size:记录了放入HashMap的元素个数
-
loadFactor:负载因子
-
threshold:阈值,决定了HashMap何时扩容,以及扩容后的大小,一般等于table大小乘以loadFactor



