你必须知道的HashMap面试题

1.HashMap的工作原理,其中get()方法的工作原理?

HashMap基于hash原理,通过put()get()方法存储和获取元素。它内部使用数组+链表或红黑树的结构,通过hash运算找到bucket位置来存储Entey对象,通过equals()方法找到正确的键值对。HashMap使用链地址法来解决hash碰撞问题,当发生碰撞时,对象会存储在链表的下一个节点处。

get()首先通过计算key的hash值找到在数组中的下标,若下标处元素为空则直接返回null,否则判断该下标处元素是否是要查找的元素,是就直接返回,不是就判断该元素节点是否为红黑树节点,是就进行红黑树节点的查找,否则遍历链表进行查找。

关键点:HashMap是在bucket中存储键对象和值对象作为Map.Entry

2.我们能否让HashMap同步?

通过Map m = Collections.synchronizeMap(hashMap)实现同步。在synchronizeMap中给HashMap的所有操作增加synchronized竞争监视器锁来实现线程同步。

关键点:synchronizeMapConcurrentHashMap

3.关于HashMap中的哈希冲突(哈希碰撞)以及冲突解决办法?

HashMap通过计算key的hash值来确定数组位置,不同的key可能会产生相同hash值,因此会发生hash碰撞,在HashMap中采用链地址法来解决hash冲突,当发生碰撞是,将相同hash值的元素存储在链表的下一个节点处。除此之外还有开放地址法,再散列法。

关键点:产生碰撞原因,一个好的hash函数可以降低碰撞。

4.如果HashMap的大小超过负载因子定义的容量会怎么办?

HashMap中默认的负载因子为0.75,当HashMap中数组元素个数超过负载因子乘以数组总容量时会发生扩容,将数组扩展为原来的两倍,对原数组中的元素再进行hash运算,获得在新数组中的位置。

关键点:扩容!!!。

5.你了解重新调整HashMap大小存在什么问题吗?

多线程条件下对于HashMap进行操作确实会存在同步问题,看网上说调整HashMap大小会导致在头部添加元素从而避免尾部循环,然后就死循环了…其实我没懂!!!

6.为什么String, Interger这样的wrapper类适合作为键?

HashMap推荐使用不可变变量作为键,其中String最为常用,因为String是不可变的,也是final的,我们通过计算key的hash值在数组中进行查找,如果key是可变类型,那么在插入和获取时返回的hashcode不同,就无法正确获得我们想要的对象。

7.我们可以使用自定义的对象作为键吗?

可以,但是要确保这个对象重写了hashCode()equals()方法,并且该对象插入Map后就不再改变,只要遵循了这些,它就已经可以作为键了。

8.我们可以使用CocurrentHashMap来代替Hashtable吗?

可以,并且推荐使用CocurrentHashMap。HashTable采用synchronized实现同步,对其中所有方法添加监视器锁,问题在于,在一个线程进行put操作时,其他线程无法获得get或其他操作,而CocurrentHashMap采用分段锁技术,比HashTable提供更强的线程安全。

9.HashMap扩容问题?

扩容是是新建了一个HashMap的底层数组,而后调用transfer方法,将就HashMap的全部元素添加到新的HashMap中(要重新计算元素在新的数组中的索引位置)。 很明显,扩容是一个相当耗时的操作,因为它需要重新计算这些元素在新的数组中的位置并进行复制处理。因此,我们在用HashMap的时,最好能提前预估下HashMap中元素的个数,这样有助于提高HashMap的性能。

10.为什么HashMap是线程不安全的?如何体现出不安全的?

当多个线程对HashMap就行put操作时,如果put的key相同会产生碰撞,那么HashMap会将两个key放在数组的同一位置,其中一个线程的key会被覆盖。

当多个线程检测到数组需要扩容时,都会进行数组中元素hash值的重新计算和数据复制,那么也势必造成最后只有一个线程创建的数组成功赋值给table。

11.能否让HashMap实现线程安全,如何做?

  1. 使用线程安全的HashTable,当一个线程访问HashTable的同步方法时,其他线程会被阻塞。也就是说当一个线程进行get操作,其他线程不能进行任何操作,效率很低。
  2. 使用Collections.synchronizeMap(hashMap)。但是它的效率也不高,不信去看源码,看完之后会发现它跟HashTable具有同样的问题。
  3. 使用CocurrentHashMap

12.HashMap中hash函数是怎么实现的?

    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

hash通过计算key自身的hashCode()值,,返回的类型是int,为了减少碰撞产生的几率,将获得的hashCode()的低16位与高16位右移16位相异或。

13.HashMap什么时候需要重写hashcode和equals方法?

当key值为对象时必须要求重写hashcode()equals()方法。因为默认的hashcode()使用的是该值在内存中的地址作为该值的hash值,如果两个具有相同意义的对象进行比较时,由于其地址不同会导致计算出的hash值也不同。而重写equals()可以确保两个对象具有相同意义的属性。

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
回答: HashMap是Java中的一个常用数据结构,它的底层是由hash数组和单向链表实现的。每个数组元素都是一个链表,通过Node内部类实现了Map.Entry接口来存储键值对。HashMap通过put和get方法来存储和获取数据。\[1\] 在重写equals方法时,我们需要同时重写hashCode方法。这是因为在HashMap中,查找value是通过key的hashCode来进行的。当找到对应的hashCode后,会使用equals方法来比较传入的对象和HashMap中的key对象是否相同。因此,为了保证正确的查找和比较,我们需要同时重写equals和hashCode方法。\[2\]\[3\] HashMap在什么时候进行扩容呢?当HashMap中的元素数量超过了负载因子(默认为0.75)与当前容量的乘积时,就会进行扩容。扩容是为了保持HashMap的性能,因为当元素数量过多时,链表的长度会变长,查找效率会下降。扩容的过程是创建一个新的数组,将原数组中的元素重新分配到新数组中,然后将新数组替换为原数组。\[3\] #### 引用[.reference_title] - *1* *2* *3* [史上最全Hashmap面试总结,51道附带答案,持续更新中...](https://blog.csdn.net/androidstarjack/article/details/124507171)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值