Java集合包

目录

 

为什么在Java面试中一定会深入考察 HashMap?

HashMap的底层数据结构是什么?

JDK1.8中对hash算法和寻址算法是如何优化的?

HashMap是如何解决hash碰撞问题的?

说说HashMap是如何进行扩容的?

ArrayList,LinkedList,TreeMap,LinkedHashMap,HashSet等底层的数据结构和各自的优势和劣势?

equals和hashcode之间的关系?


为什么在Java面试中一定会深入考察 HashMap?

HashMap的深入考察,必然是面试中的一个核心的点

都是写Java代码,基于Java都是来构建各种各样的系统的,软件的,基于Java写出来一大堆的代码,可能会访问很多其他的东西,数据库,缓存,消息中间件,核心还是来写Java代码实现一些逻辑的运转

接收到一个请求,可能会创建一些数据结构,来存放一些数据,做一些循环、跳转、判断加加减减,数据处理,逻辑,通过一大堆的逻辑就可以完成一些系统功能,或者是软件的功能

HashMap,数据结构,进行一定的逻辑的处理

一句话总结:你是Java工程师,你写代码的时候必然会用到一些数据结构,其中尤为经典的就是 Hash Map

HashMap的底层数据结构是什么?

哈希表底层数据结构实际上就是数组。它利用数组支持按照下标随机访问的时候,时间复杂度是o(1)的特性。我们通过哈希函数把元素的键值映射为下标,然后将数据存储在数组中对应下标的位置。当我们按照键值查询元素时,我们使用相同的哈希函数,将键值转化为数组下标,从对应的数组下标的位置取出数据。

JDK1.8中对hash算法和寻址算法是如何优化的?

首先来看hash算法

//JDK1.8以后的HashMap部分源码
static final int hash(Object key){
	int h;
	return (key == null)?0(h=key.hashCode())^(h>>>16);
}

假设有一个key的hash为:1111 1111 1111 1111 1111 1010 0111 1100 

右移16位后的结果位:      0000 0000 0000 0000 1111 1111 1111 1111

最终异或的结果位:          1111 1111 1111 1111 0000 0101 1000 0011 

hash算法的优化:实际就是将他的高低十六位进行异或操作,让低十六位同时保持了高低十六位的特征。同时也可以避免一些hash值后续出现冲突,结果为int类型(int 4字节 长度32位)

因为Map一般不会很大,长度(n-1)的二进制高16位一般都是0,核心计算是低16位; 这就要求寻址算法中,需要用上方异或的结果进行接下来的运算;如果用原hash进行计算,相当于原hash的高16位没有参与运算,所以才有了hash计算中的异或操作,让低十六位同时保持了高低十六位的特征,保证就算2个不同的hash低16位一致,但是高16位不同,最终异或后参与寻址

取模运算性能不够好,数学模型中 对n取模 = 对(n-1)与运算 ;而且与运算的性能很高

寻址算法的优化:寻址算法就是对长度为n的数组(这里的数组长度指的就是Map的size)取模,得到在数组中的位置。根据数学规律,对n取模,就是和n-1进行与运算。与运算的效率远远高于求模运算,所以采用与运算。而数组的长度通常没有很大,所以高位与出来都是0,如果不进行hash算法优化,那么高位的信息就会丢失。

以上算法的设计是为了尽量减少hash碰撞

HashMap是如何解决hash碰撞问题的?

hash冲突, hash碰撞:在经过hash算法和寻址算法后,发现定位到数组的位置还是一样的

为了解决这种情况,衍生了链表 O(n)和红黑树 O(logn)

在发生冲突的位置,挂载一个链表,在这个链表中放入多个元素,让多个kv同时放在数组的同一个位置;get的时候,发现这个位置是一个链表,再去遍历链表找到自己的元素。

但是链表查询慢,当链表很长时,查询就很慢,  对这种情况又引入了红黑树,在链表长度满足时会将链表转换为红黑树,从而提升性能

说说HashMap是如何进行扩容的?

hashMap底层默认是一个数组,当这个数组满了以后,就会自动扩容,变成一个更大的数组,可以在里面放更多的元素。
hashMap的默认大小是16位的,当16存满以后就会进行***2倍扩容***,变成长度为32的数组。这个时候就要对原先数组中存储的元素进行rehash,即将他们的哈希值和(32-1)进行与运算,原本在长度为16的处于相同位置的几个元素,可能就要变换位置,不在同样的位置了。
为什么进行两倍扩容?
两倍扩容就是二进制位的上一位变成1,比如
0000 0000 0000 1111
变成
0000 0000 0001 1111
在进行rehash操作时,判断二进制结果是否多了一个bit的1,如果没多,那么就是原来的index,如果多了,那么就是index + oldcap,通过这个方式,避免rehash的时候,进行取模运算,位运算的性能更高。

HashMap为什么是2倍扩容呢?

按位和运算比求余运算快是一方面,还有很重要的一点就是这种扩容方式在碰撞比较多的情况下对元素的重新分配计算操作更简单

原先在 oldTab[i] 位置的元素在resize后要么在 newTab[i] ,要么在 newTab[i+oldCap], 而且只有 oldTab[i] 上的元素会被分配到这两个位置

如果随意扩容,则每次扩容都要重新计算元素位置,然后判断位置是否为链表,然后解决hash碰撞,遍历链表或红黑树到末尾才能添加新元素,这样性能会很差

ArrayList,LinkedList,TreeMap,LinkedHashMap,HashSet等底层的数据结构和各自的优势和劣势?

 

equals和hashcode之间的关系?


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值