http://blog.csdn.net/magic_coder/article/details/6342159
阅读JAVA中HashMap的源码时,以前数据结构学的都又回到脑海中,JAVA中的HashMap是"链表散列"的结构。有些收获点在此记录。
1. 基本结构Entry为 key-value,HashMap中的Entry<K,V>结构大致如下:
static class Entry<K,V> implements Map.Entry<K,V>{
final K key;
V value;
Entry<K,V> next;
final int hash;
.......
}
HashMap这里的Entry<K,V>相当于《数据结构》中节点node,Entry包含key(常量),value,hash(哈希值)和指向下一个Entry结构的引用。这么一来,一个Entry就相当于一个链表了。
2. HashMap本身的成员变量如下:
//The table, resized as necessary. Length MUST Always be a power of two.
transient Entry[] table;
(核心结构,table是一个Entry类型的数组,每一个元素都是Entry节点的链表。)
static final int DEFAULT_INITIAL_CAPACITY = 16;
默认哈希表的初始容量,必须为2的多少次幂。为什么?正确答案:这是为了让元素在数组中分布均匀,而且为元素在数组中位置的计算速度优化做了铺垫。
考虑哈希表要添加一个元素,现在假设前提是某个Entry的hash值已经计算出来,那么我们都知道哈希表下一步就是根据这个hash值
把Entry放在table数组的某个位置。我们首先想到的就是把hash值对数组长度取模运算,这样一来,元素的分布相对来说是比较均匀的。但是,“模”运算的消耗还是比较大。我们看一下Java的牛人是怎么写的。Java源码中indexFor(int h, int length) 方法用来计算某Entry对象应该保存在 table 数组的哪个索引处,其代码如下:
这个方法简单而又非常巧妙,它通过 h & (table.length -1) 来得到该对象的保存位,而HashMap底层数组的长度总是 2 的 n 次方,这里就是HashMap在速度上优化的一个点。在 HashMap 构造器中有如下代码:
所以说,当数组长度为2的n次幂的时候,不同的key算得得index相同的几率较小,那么数据在数组上分布就比较均匀,也就是说碰撞的几率小,相对的,查询的时候就不用遍历某个位置上的链表,这样查询效率也就较高了。现在明白为什么MUST be a power of two 了吧。Java在这里充分考虑了速度的优化。
static final int MAXIMUM_CAPACITY = 1 << 30;
//The load factor used when none specified in constructor.
static final float DEFAULT_LOAD_FACTOR = 0.75f;
这个0.75是HashMap的负载因子,即数据结构做题的时候算的那个 “散列表的实际元素数目(n)/ 散列表的容量(m)”。
负载因子衡量的是一个散列表的空间的使用程度,负载因子越大表示散列表的装填程度越高,反之愈小。对于使用链表法的散列表来说,查找一个元素的平均时间是O(1+a),因此如果负载因子越大,对空间的利用更充分,然而后果是查找效率的降低;如果负载因子太小,那么散列表的数据将过于稀疏,对空间造成严重浪费。