HashMap原理及数据结构和算法解析

HashMap是一个以空间换时间,内部以数组+链表\红黑树实现的散列表。HashMap的具体原理我们不做深入仔细分析,这类文章网上较多,且HashMap在面试中命中率极高。本文以jdk1.8为例,只分析里面我认为值得拿出来分析的有关数据结构和算法的部分来讲解。

HashMap的长度

HashMap的初始默认长度是16.HashMap在jdk1.8上做了一层优化,创建时并没有创建Node数组,只有首次put元素的时候才创建。但是我们在创建时也可以指定数组的长度,HashMap会将长度重新转化为2的n次方。具体为什么是2的n次方,后面再分析。

/**
     * Returns a power of two size for the given target capacity.
     */
    static final int tableSizeFor(int cap) {
        int n = cap - 1;
        n |= n >>> 1;
        n |= n >>> 2;
        n |= n >>> 4;
        n |= n >>> 8;
        n |= n >>> 16;
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    }

这里主要运用了位或操作和无符号右移操作。
位或操作:两个位都为0时,结果才为0
无符号右移:不论正负,高位均补0.可参考博客 有符号右移>>,无符号右移>>>
tableSizeFor方法很简单,但是却充分用到了我们大一学到的大学计算机基础内容,主要是位运算和移位运算符,关于其他的可自行扩展(如左移( << )、右移( >> ) 、无符号右移( >>> ) 、位与( & ) 、位或( | )、位非( ~ )、位异或( ^ ))。
首先cap为我们传入的想要初始化数组的值,这里会对cap进行一个减一的操作,然后依次进行无符号右移和或运算。现假定我们传入默认长度16.

static final int tableSizeFor(int cap) {
        //cap:10000
        //n:1111
        int n = cap - 1;
        //n >>> 1:111   1111|111:1111
        n |= n >>> 1;
        //n >>> 2:11    1111|10:1111
        n |= n >>> 2;
        //n >>> 4:0     1111|0:1111
        n |= n >>> 4;
        //n >>> 8:0     1111|0:1111
        n |= n >>> 8;
        //n >>> 16:0    1111|0:1111
        n |= n >>> 16;
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;//return 16
    }

假定传入13

static final int tableSizeFor13(int cap) {
        //cap:1101
        //n:1100
        int n = cap - 1;
        //n >>> 1:110   1100|110:1110
        n |= n >>> 1;
        //n >>> 2:10    1110|11:1111
        n |= n >>> 2;
        //n >>> 4:0     1111|0:1111
        n |= n >>> 4;
        //n >>> 8:0     1111|0:1111
        n |= n >>> 8;
        //n >>> 16:0    1111|0:1111
        n |= n >>> 16;
        //return 16
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    }

从传入的16和13流程中得到的每一个结果,我们可以知道这里使用位或运算和无符号右移,就是把各个位都变成1。首先对于大于8但是小于等于16的数字,减去1之后,左起第4为肯定是1,这样通过不断的无符号右移,一位一位的把右边的0和1数字编程1,位或运算后,又把移位后的数字填充到当前n。
再详细说一下:对于大于8但是小于等于16,一定满足1*的位(代表0或1的任意数字)。
当进行第一步 n |= n >>>1 时,n >>> 1 = 1
,则 n |= n >>>1 = 11
.
这时n至少有前2个最高位均为1,那么就可以进行2位无符号右移。n >>> 2 = 1111,则n |= n >>>1 = 1111.
这时n至少有前4个最高位为1,那么就可以进行4位的无符号右移等等往下操作。所以这里必然可以得到一个2^n
-1的数字,最后返回 一个2^n
-1+1=2^n。逻辑结束.

扩容机制及扩容过程中的高并发问题

这里讲会在本周完善补充

猜你喜欢

转载自blog.csdn.net/codingtu/article/details/84846220