🏡 博客首页:派 大 星
⛳️ 欢迎关注 ❤️ 点赞 🎒 收藏 ✏️ 留言
🎢 本文由派大星原创编撰
🚧 系列专栏:《迎战面试》
🎈 本系列分享自己准备面试的过程和一些经典的面试题
🥞 HashMap和HashTable的区别?
- HashMap是非线程安全,而HashTable是线程安全的
- HashMap效率较高,而HashTable效率较低低
- HashMap可以存放key为null值,而HashTable不允许存放key为null值
为什么这么说呢?让我们到源码中一探究竟:
由图中可以看出,在HashMap
中的put
方法中并没有synchronized
锁,属于线程不安全的,而HashTable
中的put
方法含有synchronized
同步锁来保证在进行操作的过程 中线程是安全的。但是在多线程的条件下访问到HashTable
下的put
方法时最终结果会变成单线程,那么必然会存在一个问题,即—效率问题;紧接着再进行探究是否可以存放null值
,进入源码:
由HashTable
为例:由上图可以发现HashTable
中的put()
方法的执行逻辑,首先判断value
为null
如果是则抛出空指针异常,然后再对key
值进行hashCode
取值计算,从而获取tab[]
在某下标下的值并返回,从测试中得出最终结果:
结论:当存入key
值为null
时,则无法通过key.hashCode()
从而计算出具体下标所对应的值,所以在HashTable中的key
不能为null
值,而在HashMap
中的key
则可以存放null
值。那么HashMap
既然是允许存放key=null
,那么存放在数组的哪个位置呢?index= 0
🥗 简述Java中的HashMap
注:HashMap集合底层是使用
Entry
对象去封装键值对的
JDK8
之前底层实现时数组+链表,JDK8
改为数组+链表/红黑树。主要成员变量包括存储数据的table数组
、元素数量size
、加载因子loadFactor
HashMap中数据是以键值对的形式存在,键对应的hash值用来计算数组下标的,如果两个元素key
的hash
值一样,就会发生哈希冲突,被放到同一个链表上。
table
数组记录HashMap
的数据,每个下标对应一条链表,所有哈希冲突的数据都会存放到一条哈希链表,Node/Entry
节点包含四个成员变量:key
、value
、next指针
和hash值
,在JDK8
后链表超过8会转化为红黑树。
若当前数据/总数据容量 > 负载因子,那么HashMap
将会执行扩容
操作
默认初始化容量为16
,扩容容量必须是2的幂次方,最大容量为1<<30
,默认的加载因子为0.75
🥙 如何解决Hash冲突的问题
- 使用链地址法(使用散列表)来链接拥有相同的hash值的数据
在Java中,保存数据有两种比较简单的数据结构:数组和链表。数组的特点是:寻址容易,插入和删除困难;链表的特点是:寻址困难,但插入和删除容易;所以将数组和链表结合在一起,发挥两者各自的优势,使用一种叫链地址法的方式可以解决哈希冲突。
- 使用2次扰动函数(hash函数)来降低哈希冲突的概率。使得数据分布更平均
hashCode取值出的高位也参与运算,进一步降低hash碰撞的概率,使得数据分布更平均,我们把这种操作称为扰动
static final int hash(Object key){
int h;
//与自己右移16位进行异或运算(高低位异或)
return (key == null)? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
在JDK8
中,只进行了1次位运算和1次异或运算(2次扰动)
- 引入红黑树进一步降低遍历的时间复杂度,使得遍历更快
JDK8
在HashMap中新增了红黑树的数据结构,进一步使得遍历复杂度从O(n)
降低至O(logn)
🍲 为什么重写equals方法的时候还要重写hashCode方法
HashMap
中value
的查找是通过key
的hashCode
来查找,所以对自己的对象必须重写hashCode
方法通过hashCode
找到对象地址后会用equals
比较你传入的对象和HashMap
中的key
对象是否相同,因此还要重写equlas
.