深入理解HashMap,hash函数

来源:互联网 发布:js鼠标移入事件 编辑:程序博客网 时间:2024/04/30 11:07
Hashmap是一种非常常用的、应用广泛的数据类型,最近研究到相关的内容,就正好复习一下。网上关于hashmap的文章很多,但到底是自己学习的总结,就发出来跟大家一起分享,一起讨论。
 
粗浅的咱们不说,熟悉Java的应该都用过HashMap,实现原理就是应用散列实现键值对的映射。主要讨论下HashMap中的散列算法,首先看JDK源代码:
 
1public V put(K key, V value) {
2        if(key ==null)
3            returnputForNullKey(value);
4        inthash = hash(key.hashCode());
5        inti = indexFor(hash, table.length);
6        for(Entry e = table[i]; e !=null; e = e.next) {
7            Object k;
8            if(e.hash == hash && ((k = e.key) == key || key.equals(k))) {
9                V oldValue = e.value;
10                e.value = value;
11                e.recordAccess(this);
12                returnoldValue;
13            }
14        }
15  
16        modCount++;
17        addEntry(hash, key, value, i);
18        returnnull;
19    }

 

这是HashMap中的put方法。其中最重要的两句定位的话如下:
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
 
我们先从第一句开始分析,hash算法在HashMap中定义如下:
 

1<span style="color: #000000;">staticinthash(inth) {
2        // This function ensures that hashCodes that differ only by
3        // constant multiples at each bit position have a bounded
4        // number of collisions (approximately 8 at default load factor).
5        h ^= (h >>>20) ^ (h >>>12);
6        returnh ^ (h >>>7) ^ (h >>>4);
7    }</span>
8  
9<span style="color: #000000;">

 

先复习下Java操作符:

“^”代表异或运算,11得1 00得1 10得0;

“&”代表与运算,11得1 00得0 10得0;

“>>>”是移位运算,把1和0按位移动;

 

假设key.hashCode()的值为:0x7FFFFFFF,table.length为默认值16。
上面算法执行如下:
 

 

得到i=15 。这种算法使得最低位上原hashCode的8位都参与了^运算,所以在table.length为默认值16的情况下面,hashCode任意位的变化基本都能反应到最终hash table 定位算法中,这种情况下只有原hashCode第3位高1位变化不会反应到结果中,即:0x7FFFF7FF的i=15。
 
这样做的好处是什么?我的理解是:它的目的是让“1”变的均匀一点,散列的本意就是要尽量均匀分。先看个例子,一个十进制数32768(二进制1000 0000 0000 0000),经过上述公式运算之后的结果是35080(二进制1000 1001 0000 1000)。看出来了吗?或许这样还看不出什么,再举个数字61440(二进制1111 0000 0000 0000),运算结果是65263(二进制1111 1110 1110 1111),现在应该很明显了。至于怎么证明这个运算确实可以散列均匀。。我目前还不知道,希望能有达人帮我解答。
 
那散列的均匀又有何用?来分析下一句话:
 
int i = indexFor(hash, table.length);
 
这个indexFor方法很简单:
 

1/**
2     * Returns index for hash code h.
3     */
4    staticintindexFor(inth,int length) {
5        returnh & (length-1);
6    }

 
当计算出来的hash函数h和hashMap的length做了&运算后,会得到[0,length-1]其中的一个值,而散列的均匀也会使这个值分布的均匀,从而达到HashMap高效的一点。