目录

🥞 HashMap和HashTable的区别?

  • HashMap是非线程安全,而HashTable是线程安全的
  • HashMap效率较高,而HashTable效率较低低
  • HashMap可以存放key为null值,而HashTable不允许存放key为null值

为什么这么说呢?让我们到源码中一探究竟:
关于HashMap的面试题,来看看你都知道哪些_数组

由图中可以看出,在​​HashMap​​​中的​​put​​​方法中并没有​​synchronized​​​锁,属于线程不安全的,而​​HashTable​​​中的​​put​​​方法含有​​synchronized​​​同步锁来保证在进行操作的过程 中线程是安全的。但是在多线程的条件下访问到​​HashTable​​​下的​​put​​​方法时最终结果会变成单线程,那么必然会存在一个问题,即—效率问题;紧接着再进行探究是否可以存放​​null值​​​,进入源码:
关于HashMap的面试题,来看看你都知道哪些_数据_02
由​​​HashTable​​​为例:由上图可以发现​​HashTable​​​中的​​put()​​​方法的执行逻辑,首先判断​​value​​​为​​null​​​如果是则抛出空指针异常,然后再对​​key​​​值进行​​hashCode​​​取值计算,从而获取​​tab[]​​​在某下标下的值并返回,从测试中得出最终结果:
关于HashMap的面试题,来看看你都知道哪些_数组_03关于HashMap的面试题,来看看你都知道哪些_数据_04

结论:当存入​​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中,保存数据有两种比较简单的数据结构:数组和链表。数组的特点是:寻址容易,插入和删除困难;链表的特点是:寻址困难,但插入和删除容易;所以将数组和链表结合在一起,发挥两者各自的优势,使用一种叫链地址法的方式可以解决哈希冲突。

关于HashMap的面试题,来看看你都知道哪些_java_05

  • 使用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)​​​关于HashMap的面试题,来看看你都知道哪些_java_06

🍲 为什么重写equals方法的时候还要重写hashCode方法

​HashMap​​​中​​value​​​的查找是通过​​key​​​的​​hashCode​​​来查找,所以对自己的对象必须重写​​hashCode​​​方法通过​​hashCode​​​找到对象地址后会用​​equals​​​比较你传入的对象和​​HashMap​​​中的​​key​​​对象是否相同,因此还要重写​​equlas​​.