扫码关注公众号

java语言考点之Map1.7和1.8
08-14
464观看
01

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

在JDK1.7和JDK1.8中有所差别:在JDK1.7中,由“数组+链表”组成,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的。在JDK1.8中,由“数组+链表+红黑树”组成。当链表过长,则会严重影响HashMap的性能,红黑树搜索时间复杂度是O(logn),而链表是糟糕的O(n)。因此,JDK1.8对数据结构做了进一步的优化,引入了红黑树,链表和红黑树在达到一定条件会进行转换:当链表超过8且数据总量超过64才会转红黑树。将链表转换成红黑树前会判断,如果当前数组的长度小于64,那么会选择先进行数组扩容,而不是转换为红黑树,以减少搜索时间。

来自:容器和Map-Map 1.7和1.8
02

为什么HashMap在解决 hash 冲突的时候,不直接用红黑树?而选择先用链表,再转红黑树?

因为红黑树需要进行左旋,右旋,变色这些操作来保持平衡,而单链表不需要。当元素小于8个的时候,此时做查询操作,链表结构已经能保证查询性能。当元素大于8个的时候,红黑树搜索时间复杂度是O(logn),而链表是O(n),此时需要红黑树来加快查询速度,但是新增节点的效率变慢了。因此,如果一开始就用红黑树结构,元素太少,新增效率又比较慢,无疑这是浪费性能的。

来自:容器和Map-Map 1.7和1.8
03

HashMap默认加载因子是多少?为什么是 0.75,不是 0.6 或者 0.8 ?

Node[]table的初始化长度length(默认值是16),Loadfactor为负载因子(默认值是0.75),threshold是HashMap所能容纳键值对的最大值。threshold=length*Loadfactor。也就是说,在数组定义好长度之后,负载因子越大,所能容纳的键值对个数越多。默认的loadFactor是0.75,0.75是对空间和时间效率的一个平衡选择,一般不要修改,除非在时间和空间比较特殊的情况下:如果内存空间很多而又对时间效率要求很高,可以降低负载因子Loadfactor的值。相反,如果内存空间紧张而对时间效率要求不高,可以增加负载因子loadFactor的值,这个值可以大于1。

来自:容器和Map-Map 1.7和1.8
04

说一下HashMap 的put方法流程?

首先根据key的值计算hash值,找到该元素在数组中存储的下标;如果数组是空的,则调用resize进行初始化;如果没有哈希冲突直接放在对应的数组下标里;如果冲突了,且key已经存在,就覆盖掉value;如果冲突后,发现该节点是红黑树,就将这个节点挂在树上;如果冲突后是链表,判断该链表是否大于8,如果大于8并且数组容量小于64,就进行扩容;如果链表节点大于8并且数组的容量大于64,则将这个结构转换为红黑树;否则,链表插入键值对,若key存在,就覆盖掉value。

来自:容器和Map-Map 1.7和1.8
05

为什么HashMap是线程不安全的?

JDK1.7中,由于多线程对HashMap进行扩容,调用了HashMap#transfer(),具体原因:某个线程执行过程中,被挂起,其他线程已经完成数据迁移,等CPU资源释放后被挂起的线程重新执行之前的逻辑,数据已经被改变,造成死循环、数据丢失。JDK1.8中,由于多线程对HashMap进行put操作,调用了HashMap#putVal(),具体原因:假设两个线程A、B都在进行put操作,并且hash函数计算出的插入下标是相同的,当线程A执行完第六行代码后由于时间片耗尽导致被挂起,而线程B得到时间片后在该下标处插入了元素,完成了正常的插入,然后线程A获得时间片,由于之前已经进行了hash碰撞的判断,所有此时不会再进行判断,而是直接进行插入,这就导致了线程B插入的数据被线程A覆盖了,从而线程不安全。

来自:容器和Map-Map 1.7和1.8
06

说一下HashMap 1.7和1.8的区别

1结构区别:HashMap1.8的底层数据结构是数组+链表+红黑树。HashMap1.7的底层数据结构是数组加链表。2扩容的区别:Jdk1.7:头插法,添加前先判断扩容,当前准备插入的位置不为空并且容量大于等于阈值才进行扩容,是两个条件!扩容后可能会重新计算hash值。Jdk1.8:尾插法,初始化时,添加节点结束之后和判断树化的时候都会去判断扩容。我们添加节点结束之后只要size大于阈值,就一定会扩容,是一个条件。由于hash是final修饰,通过e.hash&oldCap==0来判断新插入的位置是否为原位置。

来自:容器和Map-Map 1.7和1.8
07

HashMap、HashTable的key和value是否可为null?

HashMap对象的key、value值均可为null。HahTable对象的key、value值均不可为null。且两者的的key值均不能重复,若添加key相同的键值对,后面的value会自动覆盖前面的value,但不会报错。因为HashMap是应用在单线程场景下,在源码中,当判断key为空时,会把value存入table[0]处。当value为空时,可以通过**containsKey(key)**来判断是否有key,若有,则说明返回的null是空value,若没有这个key,则说明返回的null是没有这个key的空。当存入key和value的都为空时,containskey(key)也会返回true,以为table中有Node节点(Node中的key和value属性都为null)

来自:容器和Map-Map 1.7和1.8
课程
专栏
【校招VIP】java基础 HashMap1.7与1.8
csdn
map
【校招VIP】JDK1.7和JDK1.8中HashMap为什么是线程不安全的?
csdn
HashMap
jdk
HashMap、HashTable的key和value是否可为null
csdn
HashMap
【校招VIP】关于HashMap扩容机制
csdn
HashMap
【校招VIP】HashMap精选面试题
HashMap 的核心面试题
知乎
HashMap
面试题
java语言-容器和Map-Map 1.7和1.8
5专栏
1课程
7 试题
热门专题