最近在用HashMap的时候,碰到一些问题,有疑惑,遂查看源代码分析了一下。
我们知道,HashMap<K, V>是以键值对来读写数据的,那它底层用于存放Value的什么?又如何判断两个Key是否相等呢?
首先说说哈希表的一些相关概念,可能有些名词会有不同的叫法。
- 容量:散列表中散列数组大小
- 散列运算:key->散列值(散列数组下标)的算法, h ^= (h >>> 20) ^ (h >>> 12);return h ^ (h >>> 7) ^ (h >>> 4);
- 散列桶:散列值相同的元素的“线性集合”
- 加载因子(loadFactor):散列数组的加载率,一般小于75%比较理想(即元素数量/散列数组大小)。
-
- threshold = (int)(newCapacity * loadFactor);//阈值=新的容量*加载因子,当新增元素时达到了阈值就要再次扩容
- 散列查找:根据Key计算散列值,根据散列值(下标)找到在散列桶,在散列桶中顺序比较Key,如果相同(Key的hascode相同、且equals()为true)就返回Value;散列表中Value可以重复,只要其对应的Key不一样即可;如果Key相同,新添加的元素会覆盖原来的Value。
再来看看HashMap类中的几个主要成员变量:
- Entry[] table;//Entry<K, V>型的数组,用于存放Value,每个Entry元素里有Key, Value, hash与next(指向下一个Entry)
- int size;//所有Value的数量,Map的实际容量
- int threshold;//阀值,超过之后要对table扩容
- final float loadFactor;//加载因子
Entry[] table
Entry | Entry | Entry | Entry | Entry |
↓
e.next
↓
e.next
↓
null
同一个散列桶中有多个Entry时
Entry:HashMap中的内部类
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
final K key;
V value;
/*Entry 这里是一个链表,也就是散列桶中的线性集合:
* 当hash code相同时,位于table的同一个下标中,能过next连成一个线性集合。
*/
Entry<K,V> next;final int hash;
/**
* Creates new entry.*/
Entry(int h, K k, V v, Entry<K,V> n) {
value = v;
next = n;
key = k;
hash = h;
}
}
HashMap的put方法(增加元素):
public V put(K key, V value) {
if (key == null)//可以以null为key
return putForNullKey(value);
int hash = hash(key.hashCode());//算出此key的hash code,用位移的算法
int i = indexFor(hash, table.length);//用hashcode&table长度,即将hashcode的高位清零,保留低位(与table长度一样)的位,这样得到的下标,一定在table的长度范围之内。
if (key == null)//可以以null为key
return putForNullKey(value);
int hash = hash(key.hashCode());//算出此key的hash code,用位移的算法
int i = indexFor(hash, table.length);//用hashcode&table长度,即将hashcode的高位清零,保留低位(与table长度一样)的位,这样得到的下标,一定在table的长度范围之内。
//找到table下标为i的位置,如果此位置已经有元素即两个元素的Key的hashcode相同,则说明此散列桶有多个元素;
//从第一个Entry开始,循环e.next遍历此链表并判断:
for (Entry<K,V> e = table[i]; e != null;
e = e.next) {
Object k;
Object k;
//如果两个Entry的hashcoe相同,且equals,那么则认为是同一个Key
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
//则把这个新元素替换原来的元素
e.value = value;
e.recordAccess(this);
e.value = value;
e.recordAccess(this);
//并返回原来的元素
return oldValue;
}
}
//如果table下标为i的位置为null,则说明此散列桶为空,直接将新元素添加到此即可
modCount++;
addEntry(hash, key, value, i);*注
return null;
}
return oldValue;
}
}
//如果table下标为i的位置为null,则说明此散列桶为空,直接将新元素添加到此即可
modCount++;
addEntry(hash, key, value, i);*注
return null;
}
*注:
void addEntry(int hash,
K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];//e为null
table[bucketIndex] = new Entry<K,V>(hash, key, value, e);//新建Entry,next为null
if (size++ >= threshold)//如果实际大小超过阀值,则扩容
resize(2 * table.length);
}
Entry<K,V> e = table[bucketIndex];//e为null
table[bucketIndex] = new Entry<K,V>(hash, key, value, e);//新建Entry,next为null
if (size++ >= threshold)//如果实际大小超过阀值,则扩容
resize(2 * table.length);
}