HashMap面试问题总结

Q1:什么是HashMap?它有哪些特点?

HashMap是Java中一种常用的数据结构,它使用键值对(key-value)的方式存储数据。每个在HashMap中只能出现一次,但值可以是任何类型的对象

主要特点:它能够通过键快速检索对应的值。

Q2:HashMap 的数据结构?

JDK7中,基于数组+链表实现,如果产生哈希冲突后,键值会放在对应元素的后面,导致链表越来越长,这样查询效率会变得越来越(时间复杂度为O(N))。所以JDK8以后采用数组+链表+红黑树。底层维护一个Node数组,当长度超过8就不再采用链表存储而采用红黑树,时间复杂度变为O(\log N),提高了查找效率。负载因子超过0.75时,进行扩容。

Q3:为什么要把临界点设置为8?

Q4:什么是负载因子,为什么要把因子设置为0.75?

HashMap的负载因子是指哈希表中每个桶的平均元素数量。在Java中,HashMap的默认负载因子为0.75。

如果加载因子比较大扩容发生的频率比较低,浪费的空间比较小发生 hash 冲突的几率比较大。比如,加载因子是 1 的时候,HashMap 长度为 128,实际存储元素的数量在 64 至 128 之间时间段比较多,这个时间段发生 hash 冲突比较多,造成数组中其中一条链表比较长,会影响性能。如果加载因子比较小扩容发生的频率比较高,浪费的空间比较多发生 hash 冲突的几率比较小。比如,加载因子是 0.5 的时候,HashMap 长度为 128,当数量达到 6 5的时候会触发扩容,扩容后为原理的 256,256 里面只存储了 65 个浪费了。

Q5:你知道 hash 的实现吗?为什么要这样实现?

JDK 1.8 中,是通过 hashCode() 的高 16 位异或低 16 位实现的:(h = k.hashCode()) ^ (h >>> 16),主要是从速度,功效和质量来考虑的,减少系统的开销,也不会造成因为高位没有参与下标的计算,从而引起的碰撞

辅助理解:(因为int是4个字节,也就是32位,大概是有40亿的空间,如果哈希函数运用的比较松散,一般是很难出现哈希碰撞的。但是现实中一个长度为40亿的数组内存是放不下的并且HashMap在扩容前的数组的默认初始值为16,因此直接拿Hashcode值来用是不现实的。因此需要做一些运算。我们右移16位也即是把高位的数据右移到低位的16位,然后与自己做异或,那就是把高位和低位的数据进行混合,以此来加大低位的随机性,同时混合后的低位掺杂了高位的特征,这样高位的信息也被变相保存了下来。用异或的原因是:保证了对象的 hashCode 的 32 位值只要有一位发生改变,整个 hash() 返回值就会改变。尽可能的减少碰撞。

Q6:HashMap线程不安全体现在哪里,产生的原因是什么?

不安全体现在:1.在jdk1.7中,在多线程环境下,扩容时会造成环形链或数据丢失。2.在jdk1.8中,在多线程环境下,会发生数据覆盖的情况。

Q7:讲一下HashMap如何扩容

1.数组的初始容量为16,以2的此方扩容。一是为了提高性能使用足够大的数组,二是使用位运算代替取模运算,提高性能

2.数组是否需要扩充是通过负载因子进行判断的,大于0.75就进行扩容

3.为了解决碰撞,链表长度大于8就将链表转为红黑树提高性能,缩小到6时就将红黑树转换为单向链表提高性能。转换成红黑树前还会先检查是否到达阈值64,没有到达这个容量就会先放弃转换,先去扩充数组。

Q8:谈一下你对红黑树的见解

  • 每个节点非红即黑
  • 根节点总是黑色的
  • 如果节点是红色的,则它的子节点必须是黑色的(反之不一定)
  • 每个叶子节点都是黑色的空节点(NIL节点)
  • 从根节点到叶节点或空子节点的每条路径,必须包含相同数目的黑色节点(即相同的黑色高度)

Q9:.拉链法导致的链表过深问题为什么不用二叉查找树代替,而选择红黑树?为什么不一直使用红黑树?

之所以选择红黑树是为了解决二叉查找树的缺陷,二叉查找树在特殊情况下会变成一条线性结构(这就跟原来使用链表结构一样了,造成很深的问题),遍历查找会非常慢。而红黑树在插入新数据后可能需要通过左旋,右旋、变色这些操作来保持平衡,引入红黑树就是为了查找数据快,解决链表查询深度的问题,我们知道红黑树属于平衡二叉树,但是为了保持”平衡”是需要付出代价的,但是该代价所损耗的资源要比遍历线性链表要少,所以当长度大于8的时候,会使用红黑树,如果链表长度很短的话,根本不需要引入红黑树,引入反而会慢。

Q10.get方法和put方法如何实现的

  • get()方法的实现:描述get()方法的工作原理。首先是计算键的哈希值,然后使用这个哈希值作为索引找到对应的桶。如果找到了匹配的键值对,则返回该键对应的值;如果遍历完整个链表仍未找到匹配的键值对,则返回null。
  • put()方法的实现:描述put()方法的工作原理。首先是计算键的哈希值,然后使用这个哈希值作为索引找到对应的桶。如果这个桶是空的,则直接将新键值对添加到这个桶中;如果这个桶中已经有其他键值对(即发生了哈希冲突),则使用链表或红黑树来存储这些键值对。在链表的头部添加新键值对可以提高查找性能。

Q11.HashTable、HashMap的区别?

HashTable和HashMap都是Java中的数据结构,它们都使用哈希表作为底层实现,但是它们之间存在一些主要的区别:

  1. 出现时间:HashTable出现的时间比HashMap要早。HashTable在JDK1.1中引入,而HashMap在JDK1.2中引入。
  2. 继承体系:HashTable和HashMap都实现了Cloneable、Serializable和Map接口,但是它们的类继承体系不同。HashMap继承了AbstractMap类,而HashTable则继承了已废弃的Dictionary类。
  3. Null键和值:在处理Null键和值方面,HashMap允许Null键和值,但在HashTable中,遇到Null键和值会抛出空指针异常。
  4. 遍历方式:Hashtable和HashMap都使用了Iterator进行遍历,但因为历史原因,Hashtable除了使用Iterator外,还使用了Enumeration。
  5. 是否提供contains方法:HashTable保留了contains、containsValue以及containsKey方法,而HashMap去掉了Hashtable的contains方法,改为了containsKey和containsValue。
  6. 扩容方式:HashTable的默认容量是11,扩容时将容量变为原来的2倍加1。而HashMap在不指定容量的情况下默认容量为16,扩容时将容量变为原来的2倍。
  7. 线程安全性:HashTable是线程安全的,可以在多线程环境下直接使用。而HashMap是非线程安全的,使用时需要自己增加同步处理。
  8. key和value是否允许null值:HashTable不允许key和value为null,而HashMap允许null作为一个entry的key或者value。

Q12.HashMap、HashSet的区别?

HashMapHashSet
实现了Map接口实现了Set接口
存储键值对仅存储对象
调用put()向map中添加元素调用add()方法想Set中添加元素
HashMap使用键(Key)计算hashcodeHashSet使用成员对象来计算hashcode值,对于两个对象来说hashcode可能相同,所以equals()方法来判断对象的相等性

  • 18
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

艺成超爱牛肉爆大虾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值