基于 JDK1.8 进行分析
首先说几个问题,然后根据问题去分析具体情况
Q1. 默认容量是多少,负载因子是多少,扩容倍数?
Q2. 底层的存储数据结构?
Q3. 如何处理 hash 冲突?
Q4. 如何计算一个 key 的 hash 值?
Q5. 数组的长度为何是 2 的幂次方?
Q6. 扩容查找过程?
开始之前,先看一下 HashMap 的几个基本属性。
/**
* 默认的初始化容量,必须是 2 的 幂次方
*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 二进制 1 向左移四位
/**
* 最大容量,左移三十位,也就是 2 的 30 次方
*/
static final int MAXIMUM_CAPACITY = 1 << 30;
/**
* 默认的负载因子
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f; //不指定的话,默认为 0.75f
/**
* Node 节点数组, 用来表示一个 key-value
* 内部类
*/
transient Node<K,V>[] table;
/**
* map 中包含键值对映射的数量
*/
transient int size;
节点数组的存储使用了一个内部类 Node 来存储,该 Node 对象包含了当前对象 key 的 hash 值,key 值,value,以及下一个对象的指针。源码如下
static class Node<K,V> implements Map.Entry<K,V> {
final int hash; //节点 hash
final K key; // key
V value; // value
Node<K,V> next; //下一个节点
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
public final K getKey() {
return key; }
public final V getValue() {
return value; }
public final String toString() {
return key + "=" + value; }
public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
}
public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
public final boolean equals(Object o) {
if (o == this)
return true;
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
if (Objects.equals(key, e.getKey()) &&
Objects.equals(value, e.getValue()))
return true;
}
return false;
}
}
由上面的源码可以看出,每一个节点都存储了下一个节点,是一种链表形式的存储。注意到它重写了