数据结构
JDK7:数组 + 链表
JDK8:数组 + 链表 + 红黑树
本文中的源码都是 JDK8 的。
源码解析
重要的成员变量
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {
// 默认初始容量 16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
// 最大容量
static final int MAXIMUM_CAPACITY = 1 << 30;
// 默认负载因子 0.75
static final float DEFAULT_LOAD_FACTOR = 0.75f;
// 当链表中的元素个数大于等于 8,并且数组的长度大于等于 64 时将链表转为红黑树
static final int TREEIFY_THRESHOLD = 8;
// 当链表中的元素个数大于等于 8,并且数组的长度大于等于 64 时将链表转为红黑树
static final int MIN_TREEIFY_CAPACITY = 64;
// 当红黑树的长度小于 6 时转为链表
static final int UNTREEIFY_THRESHOLD = 6;
// 第一次使用时,才进行初始化操作
transient Node<K,V>[] table;
// 阈(yu)值,由负载因子和容量决定:CAPACITY * LOAD_FACTOR,默认为 16 * 0.75 = 12
// 当哈希桶数组内的节点数大于该值时,则扩容
int threshold;
}
构造方法
HashMap 的构造方法中没有对 table 进行初始化操作。table 的初始化操作是在 putVal() 方法进行的。
// 无参构造
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
// 指定初始容量
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
// 指定集合转化为 Map
public HashMap(Map<? extends K, ? extends V> m) {
this.loadFactor = DEFAULT_LOAD_FACTOR;
putMapEntries(m, false);
}
// 指定初始容量和加载因子
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;
// tableSizeFor 方法很巧妙,下文详解
this.threshold = tableSizeFor(initialCapacity);
}
tableSizeFor 方法详解
// 返回一个大于 cap 的最小的 2 的 n 次幂,比如 cap=100,则返回 128。
static final int tableSizeFor(int