1.HashMap原理
2.HashMap底层实现
3.负载因子和扩容策略,介绍各个变量的作用
4.负载因子为什么是0.75,为什么数组长度为2的指数倍
以上四连发请见小白番外篇博客:HashMap源码解读
5.HashMap是否线程安全?为何线程不安全?线程不安全的表现有哪些?
不安全!因为并发条件下存在问题!
具体表现:
(1)如果有两个线程A和B,都要插入数据,刚好这两条不同的数据经过哈希计算后得到的hash值相同,且该hash值对应的数组位置还没有Entry对象。此时线程A先通过put()方法中的if判断,该位置没有哈希冲突,然后进入if语句,这时CPU把资源又让给了线程B,线程A就停在了if语句里面,线程B判断该位置没有哈希冲突(线程A的数据还没插入),然后执行插入操作。线程B执行完后,轮到线程A执行,因为已经做过if判断了,所以现在线程A直接在该位置插入。会发生的情况就是线程A把线程B插入的数据给覆盖了,发生了线程不安全情况。
(2)如果在扩容时,在数据从旧数组复制到新数组过程中,另一个线程突然要插入一条数据,这时候是插入到新数组中,但是在数据复制过程中,HashMap是没有检查新数组上的位置是否为空,所以另一个线程新插入的数据会被后面从旧数组中复制过来的数据给覆盖掉。
(3)在扩容建立完新数组的时候,另一个线程就立刻想删除以前插入的某个元素,此时会出现删除不了的情况,因为table已经指向了新数组,而这时新数组还没有将旧数组中的数据复制过来。
(4)有两个线程A/B同时执行put()操作,但插入的元素超过了阈值需要扩容,两个线程都进入了transfer()环节,此时线程A先挂起,线程B执行完扩容操作后就可能形成链表死循环,因为在扩容前线程A插入的元素的next指向的下一个元素在新数组中顺序已经颠倒,线程A插入的元素在新数组中链表最后一个位置,此时就形成了链表死循环。在进行get()操作时,如果在此数组位置上的链表找不到对应key的value值就会发生for死循环。
6.hashCode代表什么?怎么找到hashcode位置?hashCode和equals原理,为什么要重写它们?写了equals不写hashCode有什么后果,写了hashCode不写equals有什么后果?怎样通过hashCode方法和equals方法确定value里存放的对象是否相等?
(1)hashCode在hash表中有对应的位置,可以通过取模运算得到。而对象的hashCode是通过将该对象的内部地址先转换成一个整数,然后该整数通过hash函数的算法得到hashCode,返回的hashCode值与数组长度减一进行与运算,就得到该对象存储在数组中的位置。但是在使用hashCode的时候也要注意不能在执行期间修改与hashCode值有关的对象信息,因为一旦改变,hashCode值也会改变,如果此时想执行删除操作就会删除不了,发生内存泄漏。如果非要修改,则必须将对象先从集合中删除,更新信息后再加入到集合中。
(2)默认的equals方法是Object的方法,比较的是内存地址;而默认的hashcode方法返回的是对象的内存地址转换成的一个整数,所以两个方法可以理解为比较的都是内存地址,如果不重写就无法保证元素的唯一性。可以假设两种情况验证一下,第一种比如我new了两个Person(“张三”),因为没有重写hashCode方法,两个new的对象存储地址肯定不相同,所以都会被存入数组的不同位置,这时元素就重复了。第二种是重写了hashCode方法,但没重写equals方法,照样new两个Person("张三"),此时他们hashCode值相同,但在equals比较时由于它们存储地址不同,就会以链表形式存入一个桶中,此时元素就重复了。
因此一般都需要进行重写,hashCode方法的重写主要实现就是以31作为权,与key的每一位字符的ASCII值进行相加,返回的 hashCode 值就肯定不一样。其中这个31就相当于32减一,转换成二进制以后它的最高位必然为0,所以在做乘法运算时就不会发生溢出造成key信息的丢失。然后equals方法重写主要是让equals方法比较key的类型、存储地址以及对应的value内容是否相同,进行综合判断。
(3)hashCode值与equals返回值关系:
若两个对象equals()返回true,则hashCode()一定返回相同的整数。
若两个对象equals()返回false,则hashCode()不一定返回不同的整数。
若两个对象hashCode()返回相同的整数,则equals()不一定返回true。
若两个对象hashCode()返回不同的整数,则equals()一定返回false。
7.HashMap插入的key可以为null吗?value可以为null吗?
value可以为null,而key为null时会存入数组索引为0的位置。
8.HashMap能插入重复元素吗,key可以重复?value可以重复?
在执行期间如果修改与hashCode值有关的对象信息,则hashCode值也会改变,之后如果再对该对象进行修改,就会以新的hashCode值进行插入而不会修改原来的对象,此时数组中就会存在两个key相同的对象,而且它们的value也能重复。但正常情况下当key和hashCode值相同时,只会覆盖原来的value。
9.实现一个保证迭代顺序的HashMap
LinkedHashMap,它继承于HashMap,保留了HashMap的所有特性,除此之外它是一个双向链表,用头结点记录第一个插入元素的位置,然后每个元素都会使用before和after两个指针分别指向前一个元素和后一个元素,以此保证迭代顺序。
10.如何实现线程安全的HashMap
Hashtable、ConcurrentHashMap、SynchronizedMap(三者性能:ConcurrentHashMap > SynchronizedMap > Hashtable);
此时面试官大概又会发散到Hashtable线程安全吗?HashMap和Hashtable的区别?ConcurrentHashMap底层实现?jdk1.9中ConcurrentHashMap加了哪些新特性……:)