你必须知道的HashMap面试题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/swpu_ocean/article/details/85143930

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()可以确保两个对象具有相同意义的属性。

展开阅读全文

没有更多推荐了,返回首页