本系列基于JDK8源码进行分析,HashMap
类常量定义如下:
- 最大容量
static final int MAXIMUM_CAPACITY = 1 << 30
// 所有箱子的最大容量,必须为2^n。 - 默认容量
static final int DEFAULT_CAPACITY = 1 << 4
// 所有箱子的默认容量,必须为2^n。 - 负载因子
static final float LOAD_FACTOR = 0.75f
// 负载因子 = 总键值对数 / 箱子个数,因此初始状态下,键值对数量大于12时,会触发扩容。 - 由链表转换成树的阈值
static final int TREEIFY_THRESHOLD = 8
// 当箱子中的链表长度大于 8 时,有可能会转化成树。 - 由数转换成链表的阈值
static final int UNTREEIFY_THRESHOLD = 6
// 当执行resize操作时,当箱子的链表长度少于UNTREEIFY_THRESHOLD时使用链表来代替树。默认值是6。 - 链表转换成树的最小的键值对数量
static final int MIN_TREEIFY_CAPACITY = 64
// 在转变成树之前,还会有一次判断,只有键值对数量大于 64 才会发生转换。这是为了避免在哈希表建立初期,多个键值对恰好被放入了同一个链表中而导致不必要的转化。
成员变量
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {
// 存放KV键值对的数组,每一个table槽称为桶/箱子。
transient Node<K,V>[] table;
// 当被调用entrySet时被赋值。
transient Set<Map.Entry<K,V>> entrySet;
// 存放在map中的KV键值对映射的总数。
transient int size;
// HashMap被结构性修改的次数。(结构性修改是指改变了KV映射数量的操作或者修改了HashMap的内部结构(如rehash)。这个用于fail-fast。
transient int modCount;
// 扩容阈值,当需要resize时的阈值。即当HashMap中KV映射的数量(即size)超过了threshold就会resize。threshold=capacity*loadFactor。
int threshold;
// 装载因子
final float loadFactor;
}
构造函数
4个构造函数都只是对loadFactor
赋值,并未对table
进行实例化。
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity); // 扩容的阈值,此处tableSizeFor方法返回的是大于等于指定容量的最小的2的次方数,resize方法会重新赋值
}
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
public HashMap(Map<? extends K, ? extends V> m) {
this.loadFactor = DEFAULT_LOAD_FACTOR;
putMapEntries(m, false);
}