Java:HashMap解析

参考:Java中HashMap的实现原理

参考:一文读懂HashMap

参考:漫画:高并发下的HashMap

1.概念理解

什么是Hash算法?

在讲解HashMap前我们先要对一些知识有一些基础的概念,比如什么是Hash算法。

Hash算法简单的讲是你输入任意长度的一个值后,通过散列算法返回另一个固定长度的值。

2.HaspMap的实现原理

1.HashMap概述

HaspMap在Java1.7版本是以数组+链表实现的。在Java1.8版本时候进行了改进为数组+链表+红黑树实现。

Java:HashMap解析_第1张图片 Java1.7 HasMap

从上图可以看出HashMap是数组链表实现的。当我们新建一个HashMap对象时候都会初始化一个数组。

    /*表,第一次使用时初始化,并根据需要调整大小。
    *当分配时,长度总是2的幂。
    * (在某些操作中,我们还允许长度为零,以允许当前不需要的引导机制。)
    * */
    transient Node[] table;

    static class Node implements Map.Entry {
        final int hash;
        final K key;
        V value;
        Node next;
        ...
    }

而数组的每一给对象又是一个链表。

2.HashMap的存储与读取

       根据hash值得到这个元素在数组中的位置(即下标),如果数组该位置上已经存放有其他元素了,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。如果数组该位置上没有元素,就直接将该元素放到此数组中的该位置上。

        那么怎么读取key所对应的value也知道了,根据key值计算下标然后从链表中拿取对应的value。这里发现一个问题,假如key1和key2两个不同的key计算的hash值相同怎么办,假如计算的hash值相同那么则会调用key的equals方法来比较。

3.HashMap的扩容

当HashMap存储的数据越来越多,数组同一下标下元素也越来越多。为了提高查询的效率(最好数组每个下标就一个元素),需要对数组进行扩容。那么什么情况下数组会进行扩容呢?扩容多少?

那么hashmap什么时候进行扩容呢?当hashmap中的元素个数超过数组大小*loadFactor时,就会进行数组扩容,loadFactor的默认值为0.75,也就是说,默认情况下,数组大小为16,那么当hashmap中元素个数超过16*0.75=12的时候,就把数组的大小扩展为2*16=32,即扩大一倍,然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操作,所以如果我们已经预知hashmap中元素的个数,那么预设元素的个数能够有效的提高hashmap的性能。比如说,我们有1000个元素new HashMap(1000), 但是理论上来讲new HashMap(1024)更合适,不过上面annegu已经说过,即使是1000,hashmap也自动会将其设置为1024。 但是new HashMap(1024)还不是更合适的,因为0.75*1000 < 1000, 也就是说为了让0.75 * size > 1000, 我们必须这样new HashMap(2048)才最合适,既考虑了&的问题,也避免了resize的问题。

 

4.Java1.8 HashMap

上述讲的都是Java1.7的HashMap Java1.8对ashMap进行了改进,改进后的结构图如下:

Java:HashMap解析_第2张图片 Java1.8 HashMap

Java1.8环境下 HashMap底层结构为:数组+链表+红黑树。

当数组元素下的链表长度大于TREEIFY_THRESHOLD(默认值8)时候,链表会自动转化为红黑树。

为什么要这样改进呢?我们可以假设一种情况,HashMap内包含1000个元素而这1000个所计算出的下标都相同。那么,每次我们去查询数据都要在这个链表中查询时间复杂度为O(n)。而将链表转化为红黑树后时间复杂度为O(lgn)。

那为什么不直接都用红黑树呢?

因为红黑树需要进行左旋,右旋操作, 而单链表不需要,
以下都是单链表与红黑树结构对比。
如果元素小于8个,查询成本高,新增成本低
如果元素大于8个,查询成本低,新增成本高

 

Java1.8 还改1.7的头插法为尾插法

那么为什么要这样做呢?因为JDK1.7是用单链表进行的纵向延伸,当采用头插法就是能够提高插入的效率,但是也会容易出现逆序且环形链表死循环问题。但是在JDK1.8之后是因为加入了红黑树使用尾插法,能够避免出现逆序且链表死循环的问题。

你可能感兴趣的:(Java:HashMap解析)