HashMap底层解析(包含1.7)
链表的每个节点就是一个Entry,其中包括:键key、值value、键的哈希码hash、执行下一个节点的引用next四部分
static class Entry<K, V> implements Map.Entry<K, V> {
final K key; //key
V value;//value
Entry<K, V> next; //指向下一个节点的指针
int hash;//哈希码
}
内部成员变量含义
JDK1.7中HashMap的主要成员变量及其含义
public class HashMap<K, V> implements Map<K, V> {
//哈希表主数组的默认长度
static final int DEFAULT_INITIAL_CAPACITY = 16;
//默认的装填因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//主数组的引用!!!!
transient Entry<K, V>[] table;
int threshold;//界限值 阈值
final float loadFactor;//装填因子
public HashMap() {
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}
}
put()方法
调用put方法添加键值对。哈希表三步添加数据原理的具体实现;是计算key的哈希码,和value无关。特别注意:
-
第一步计算哈希码时,不仅调用了key的hashCode(),还进行了更复杂处理,目的是尽量保证不同的key尽量得到不同的哈希码
-
第二步根据哈希码计算存储位置时,使用了位运算提高效率。同时也要求主数组长度必须是2的幂)
-
第三步添加Entry时添加到链表的第一个位置,而不是链表末尾
-
第三步添加Entry是发现了相同的key已经存在,就使用新的value替代旧的value,并且返回旧的value
public class HashMap {
public V put(K key, V value) {
//如果key是null,特殊处理
if (key == null) return putForNullKey(value);
//1.计算key的哈希码hash
int hash = hash(key);
//2.将哈希码代入函数,计算出存储位置 y= x%16;
int i = indexFor(hash, table.length);
//如果已经存在链表,判断是否存在该key,需要用到equals()
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
//如找到了,使用新value覆盖旧的value,返回旧value
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;// the United States
e.value = value;//America
e.recordAccess(this);
return oldValue;
}
}
//添加一个结点
addEntry(hash, key, value, i);
return null;
}
final int hash(Object k) {
int h = 0;
h ^= k.hashCode();
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
static int indexFor(int h, int length) {
//作用就相当于y = x%16,采用了位运算,效率更高
return h & (length-1);
}
}
标addEntry()方法
添加元素时如达到了阈值,需扩容,每次扩容为原来主数组容量的2倍
void addEntry(int hash, K key, V value, int bucketIndex) {
//如果达到了门槛值,就扩容,容量为原来容量的2倍 16---32
if ((size >= threshold) && (null != table[bucketIndex])) {
resize(2 * table.length);
hash = (null != key) ? hash(key) : 0;
bucketIndex = indexFor(hash, table.length);
}
//添加节点
createEntry