1、构造函数(默认)
static final int DEFAULT_INITIAL_CAPACITY = 16;
static final float DEFAULT_LOAD_FACTOR = 0.75f;
public HashMap() {
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}
默认的大小为16,负载因子为0.75.
2、哈希算法
final int hash(Object k) {
int h = 0;
if (useAltHashing) {
if (k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}
h = hashSeed;
}
h ^= k.hashCode();
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
9次扰动处理=4次位运算+5次异或。
此算法加入了高位计算,防止低位不变,高位变化时,造成的 hash 冲突。
hashCode值相同,这里的哈希结果值也相同。
3、put方法
public V put(K key, V value) {
//HashMap 允许存放 null 键和 null 值。
if (key == null)
return putForNullKey(value);
int hash = hash(key);
//h & (length-1) 运算确定对应 table 中的索引
int i = indexFor(hash, table.length);
//如果 i 处的 Entry 不为 null,通过循环不断遍历 e 元素的下一个元素。
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
// 到了这里,说明上面循环遍历都没有进入if条件
modCount++;
// 将 key、 value 添加到 i 索引处
addEntry(hash, key, value, i);
return null;
}
4、indexFor方法
static int indexFor(int h, int length) {
return h & (length-1);
}
当 length 总是 2 的 n 次方时, h& (length-1)运算等价于对 length 取模。
5、addEntry方法
void addEntry(int hash, K key, V value, int bucketIndex) {
//扩容条件判断
if ((size >= threshold) && (null != table[bucketIndex])) {
//把 table 对象的长度扩充到原来的 2 倍。
resize(2 * table.length);
hash = (null != key) ? hash(key) : 0;
bucketIndex = indexFor(hash, table.length);
}
//添加节点
createEntry(hash, key, value, bucketIndex);
}
这个方法用于添加节点,也负责扩容。
6、createEntry方法
void createEntry(int hash, K key, V value, int bucketIndex) {
//获取指定索引处的Entry
Entry<K,V> e = table[bucketIndex];
//将新创建的 Entry 放入 bucketIndex 索引处,并让新的 Entry 指向原来的 Entry(头插法)
table[bucketIndex] = new Entry<>(hash, key, value, e);
size++;
}
新创建一个节点,并让新节点的next指针指向e
new Entry<>(hash, key, value, e);
7、putForNullKey方法
private V putForNullKey(V value) {
for (Entry<K,V> e = table[0]; e != null; e = e.next) {
if (e.key == null) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(0, null, value, 0);
return null;
}
当put一个 key 为 null 时,调用 该方法,将 value 放置在数组第一个位置。