还不会HashMap?一篇轻松应对面试官

1、hashmap底层架构

在jdk1.7之前,hashmap底层是由数组+链表组成。

在jdk1.8之后,hashmap有数组+链表+红黑树组成。引入链表是为了解决hash冲突,引入红黑树是由于链表过长时,查询效率低,红黑树查询效率较高,时间复杂度为O(log n)。

当数组长度小于64时,链表长度大于8时,hashmap会进行扩容,从新计算hash值;

当数组长度大于64时,链表长度大于8时,链表就会转化为红黑树,以此来提高查询效率。

hashmap的默认初始容量为8,最大容量为2的30次方,负载因子为0.75。

hashmap的put()过程:

(1)首先由key通过哈希函数计算出一个hashcode;

(2)再计算出在数组中的位置index,index=hashcode&(length-1);

(3)根据key,value,hashcode生成一个entry对象

(4)如果计算出数组中的位置为空,直接 赋值给table[i]

(5)如果计算出的数组中的位置不为空,将entry对象插入到对应的链表中。

hashmap的get()过程:

(1)首先也是根据 key 计算出 hashcode,然后定位到具体的桶中。

(2)判断该位置是否为链表。

(3)不是链表就根据 key、key 的 hashcode 是否相等来返回值。

(4)为链表则需要遍历直到 key 及 hashcode 相等时候就返回值。

(5)啥都没取到就直接返回 null 。

(1)为什么数组长度需要大于64时,链表才会转化为红黑树?

因为由链表转为红黑树的过程中,节点需要不断的左旋、右旋或者变色,如果数组长度比较小且链表长度大于8时就会转化为红黑树的话,那么它的效率反而会变慢。

(2)为什么hashmap的默认初始容量必须为2的次幂

hashmap通过一个方法来确认数组中的存储位置,而这个方法是通过h&(length-1)运算来确定数组中的位置。如果默认初始容量为2的次幂,那么length-1的低位都是1,那么位运算出现的碰撞的几率较低,如果初始容量不是2的次幂的话,那么碰撞的概率就会比较高。

 static int indexFor(int h, int length) { // h 为key 的 hash值;length 是数组长度
        return h & (length-1);  
 }

注释:&(位与)运算是指相同位数都是1时为1,否则为0。

(3)为什么链表转红黑树的边界值是8?

和hashcode碰撞次数的泊松分布有关,主要是寻找一种时间和空间上的平衡。

红黑树中的TreeNode是链表中的Node所占空间的2倍,虽然红黑树的查找效率为o(logN),要优于链表的o(N),但是当链表长度比较小的时候,即使全部遍历,时间复杂度也不会太高。固,要寻找一种时间和空间的平衡,即在链表长度达到一个阈值之后再转换为红黑树。
之所以是8,是因为Java的源码贡献者在进行大量实验发现,hash碰撞发生8次的概率已经降低到了0.00000006,几乎为不可能事件,如果真的碰撞发生了8次,那么这个时候说明由于元素本身和hash函数的原因,此时的链表性能已经已经很差了,操作的hash碰撞的可能性非常大了,后序可能还会继续发生hash碰撞。所以,在这种极端的情况下才会把链表转换为红黑树,链表转换为红黑树也是需要消耗性能的,为了挽回性能,权衡之下,才使用红黑树,提高性能的,大部分情况下hashMap还是使用链表。

(4)为什么红黑树转链表的边界值是6?

 主要是因为,如果也将该阈值设置于8,那么当hash碰撞在8时,会反生链表和红黑树的不停相互激荡转换,白白浪费资源。中间有个差值7可以防止链表和树之间的频繁转换,
假设一下:
如果设计成链表个数超过8则链表转换成树结构,链表个数小于8则树结构转换成链表,如果HashMap不停的插入,删除元素,链表个数在8左右徘徊,就会频繁的发生红黑树转链表,链表转红黑树,效率会很低下。

(5)hashmap和hashtable的区别?

1、hashmap是线程不安全的,hashtable是线程安全的,因为hashtable的put()方法中加了synchronized关键字。

2、hashmap计算index是时通过按位与运算(h&length-1),而hashtable是取余运算(h%length)

3、hashmap允许null值,而hashtable不允许有null值。

 public synchronized V put(K key, V value) {
        // Make sure the value is not null
        if (value == null) {
            throw new NullPointerException();
        }

        // Makes sure the key is not already in the hashtable.
        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        Entry<K,V> entry = (Entry<K,V>)tab[index];
        for(; entry != null ; entry = entry.next) {
            if ((entry.hash == hash) && entry.key.equals(key)) {
                V old = entry.value;
                entry.value = value;
                return old;
            }
        }

        addEntry(hash, key, value, index);
        return null;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值