interview questions -(1)hashmap

HashMap是采用数组+l链表方式实现的数据结构,它是在HashTable基础上来的的,我们都知道(vshe就是vector、stack、hashtable、enomoration这四个类是线程安全的)如果想要实现线程安全的HashMap,可以通过Collertion的静态方法synchronizedMap获取线程安全的HashMap

1.关于HashMap的实现原理

HashMap的主干是一个Entry数组。Entry是HashMap的基本组成单元,每一个Entry包含key-value键值对,简单的来说就是HashMap就是由数组加链表实现的,数组是HashMap的主体,链表则是为了解决哈希冲突而存在的(两个Key算出的hash值%上数组的长度一样放在数组的同一个位置,在这个数组位置上是一个链表),如果定位到的数组位置不含链表,也就是Entry的next指向NULL,那么对于查找添加操作会很快,仅需要一次寻址就够了,如果定位的数组包含链表,对于添加操作,其时间复杂度依然为O(1),因为最新的Entry会插在链表的头部,仅需要简单的改变链表即可,而对于查找操作来说,此时需要遍历对应Entry数组位置的链表,通过KEY对象的equals方法逐个进行对比查找。所以,性能考虑,HashMap中的链表出现的越少,查询的速度就更快一些。

2.对于HashMap中这种数组长度与链表长度这个矛盾体,有加载因子这一个来调控(加载因子就是当这个Entry数组的填充度,当填充度达到了这个阈值的时候,这个Entry数组会扩容)。加载因子越大,填满的元素越多,好处是空间利用率提升了,但是冲突的机会也就会加大了,链表的长度会更长了,那么查找效率也就会随之降低了,反之,如果加载因子小的话,当这个数组还没有填充满的时候就会扩容,那么空间利用率就会降低,但是好处是hash冲突的机会减小了

冲突的机会越大,那么查找的成本就会越高,因此,必须在冲突的机会和空间利用率直接寻找一种平衡和折中,折中平衡和折中实际上也就数组结构中的时空矛盾的折中,如果机器内存足够就可将加载因子放小一点降低空间利用率来提高查询速度也就是减小哈希冲突,如果内存紧张,就可以提高加载因子从而提高空间利用率,但是也会带来查询速度的降低,一般取默认值0.75

3.put方法,当试图将一个key-value放入HashMap中的时候,程序首先根据Key的hashcode()返回值确定该Entry的存放位置,如果两个Entry的hashcode返回值相同,那么他们的存储位置相同。如果至两个Entry的key通过equals返回true,新添加的value覆盖原有的value,但是key不会覆盖。如果返回false,也就是这两个entry的key不一样,新添加的entry就会和原有的entry形成entry链,新添加的位于链的头部,通过key的hashcode值计算hash码,然后通过hash码与这个entry数组的长度-1进行&运算就可以得到对应存储在数组中的索引(一般对哈希表的散列自然会想到hash值对length取模也即是除法散列法)HashTable中是这样实现的,这种方法基本能保证元素在哈希表中散列的比较均匀,但是取模会用到除法运算效率比较低,hashmap中则是通过hash值&上这个数组长度-1来得出索引位置,这个位运算的性能比乘除的运算效率要高,hashmap中存在大量位运算

4.hash表的容量为什么是2的整数次幂,首先如果length是2整数次幂的话,那么hash值&length-1相当于对length取模,这样保证散列均匀的同时,也提升了效率,length为2整数次幂是偶数,这样length-1就是奇数,奇数的最后一位是1,这样可以保证&预算的最后一位可能是0也可能1,如果length-1是偶数的话,那么最后一位是0,无论你的hash值是啥最后一位都是0,这样浪费了接近一般的空间,因此取2的整数次幂,是为了使不同hash值发生碰撞的可能性降低

5.Resize(),前面说了关于加载因为空间时间的矛盾解释,当放生hash冲突并且size大于阈值(加载因子)时候,需要进行数组扩容,扩容时,需要新建一个长度为之前数组2倍的数组,然后将当前的entry数组中全部元素传输过去,因为扩容后的数组长度是现在两倍,那么扩容也是很耗费资源的,(所以加入我们预先知道了HashMap中元素的个数,那么预设元素的个数能够邮箱的提高HashMap的性能)

6.get 从hashmap中get元素的时候,首先会计算key的hashcode值,找到数组中对应位置的某一个元素,然后通过key的equals方法再对应位置的链表中找到需要的元素,如果key为null的话,直接去table[0]去检索,get方法步骤即算出key的hashcode,然后算出hash,然后计算出Indexfor然后找到索引位置,查看是否有链表,遍历链表,找到key的equals方法找到对应的记录(这里存在一个问题,如果传入的key对象重写了equals方法但是没有重写hashcode,恰好该对象定位到这个数组位置,如果仅仅使用equals判断可能是相等的,但是hashcode与当前对象的不一致,这种情况,根据object的hashcode的约定不能放回当前对象,应该返回null)重写equals方法也要同时覆盖hashcode

你可能感兴趣的:(interview questions -(1)hashmap)