【JAVA基础】HashMap原理

HashMap是什么

HashMap在java是很长用的数据结构,简单的看他就是一个数组,然后对插入的元素计算出来一个对应hash值,根据这个hash值对数组的长度进行取模,然后就会得到一个下标,通过这个下标就会定位到数组里的具体的一个地方,这个地方存放的就是这个元素。其实HashMap的底层是数据结合链表的来表示的。

HashMap Demo

HashMap<String, String> map = new HashMap<String, String>();
map.put(“key1”, “value1”);
map.put(“key2”, “value2”);

HashMap的Hash优化

在使用HashMap的时候,要put一个数据。比如 map.put(“key”, “value”);那么如何对这个key计算它的hash值呢,在java8以后,对key进行hash的源码是这样

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

怎么理解呢,就是把这个key原来的hash值,和key右移动16位之后,做个异或运算。
比如key的hashcode为
key 1111 1111 1111 1111 1111 1010 0111 1100
右移16位 0000 0000 0000 0000 1111 1111 1111 1111
异或结果 1111 1111 1111 1111 0000 0101 1000 0011
这样得到的才是这个key的二进制表示。把这个二进制转为int类型的值就是hash的值。

HashMap的寻址优化

HashMap的寻址算法优化

int index = (n - 1) & hash;

通过上面的逻辑就可以得到这个key在数组的一个位置。这个怎么理解呢,就是把这个数组的长度减去1然后在和它的hash值进行与运算。我们知道之前的寻址方式是hash%数组的长度,hash%数组的长度 和 (n - 1) & hash的效果是一样的。但是后面这种方法的性能更高。
那么考虑一下为什么不用直接hashcode得到值去作为hash值呢,比如
key的hashcode值 1111 1111 1111 1111 1111 1010 0111 1100
n-1的值 0000 0000 0000 0000 0000 0000 0000 1111
&操作是只有都一样才是1,我们发现,key的最初是的hashcode值和n-1的值的高16位都不一样,那么就是说高16位不进行任何运算,只有低16位是有效的。
这样可能会有一个问题,现在有两个key的hashcode值,高16位都差不多,低16位也很接近,那么它们和n-1做&运算,可能会出现这两个key找到的index是同一个情况。
那么用右移动16位之后的结果和本身做异或运算,那么得到的hash值的低16位其实包含了原数的高16位和低16位的特征。然后在和n-1做&运算。就避免了高低16位相近的两个数,可能会出现hash冲突。

HashMap解决Hash冲突

解决hash冲突的办法是用链表加上红黑树。
map在put的时候,会通过hash寻址优化算法,去避免hash冲突,然后把这个key算出的hash值,放在数组。
那么出现不同的key算出来的hash值是一样的,或者多个key算出来的还是一样的。那么就会在数组这个元素的位置放一个链表。这个链表里面放入多个元素,让多个key-value对,同时放在数组的一个位置里
当链表的长度大于8的时候,这个链表会转换为红黑树,为什么是8呢,这个是基于泊松分布的出来的最优值。

HashMap的扩容机制和rehash详解

当然,阿里巴巴的编程手册上建议创建hashMap的是初始化一个大小,那么这个hashMap的数组长度默认是16,那么满了之后,他会自动的进行2倍扩容,如果你指定的的大小不是2的n次方倍,那么HashMap会自己常见一个你给的数的最近的2的n次方倍的数,并且大于你给的值。比如你给了一个30,它就会创建一个32大小的数组。
用实例看一下hash冲突的情况。
现在有两个hash值分别是
1111 1111 1111 1111 0000 1111 0001 0101
1111 1111 1111 1111 0000 1111 0000 0101
数组长度的n-1是
0000 0000 0000 0000 0000 0000 0000 1111
那么&的结果都是000 0000 0000 0000 0000 0000 0000 0101 = 5,也就是这个key在数组的位置。
当这个数组满了,这儿的满是大于数组的长度*加载因子=16*0.75=12的时候,就自动二倍扩容。
此时的时候会这个HashMap会进行rehash操作。
rehash和之前的一样,数组中的所有元素和新的长度32-1做&操作。
刚刚两个hash的值
1111 1111 1111 1111 0000 1111 0000 0101
1111 1111 1111 1111 0000 1111 0001 0101
现在的n-1
0000 0000 0000 0000 0000 0000 0001 1111
做&操作得到的结果分别为
0000 0000 0000 0000 0000 0000 0000 0101 = 5
0000 0000 0000 0000 0000 0000 0001 0101 = 21

posted @ 2020-02-28 09:04  晓看天色暮看云  阅读(181)  评论(0编辑  收藏  举报