构造函数
构造函数
HashMap()
该构造函数意在构造一个具有
/**
* Constructs an empty HashMap with the default initial capacity
* (16) and the default load factor (0.75).
*/
public HashMap() {
//负载因子:用于衡量的是一个哈希表的空间的使用程度
this.loadFactor = DEFAULT_LOAD_FACTOR;
//HashMap进行扩容的阈值,它的值等于 HashMap 的容量乘以负载因子
threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
// HashMap的底层实现仍是数组,只是数组的每一项都是一条链
table = new Entry[DEFAULT_INITIAL_CAPACITY];
init();
}
在构造函数中都会涉及初始容量
和负载因子
,这两个参数是影响
此外,每次新建一个
static class Entry<K,V> implements Map.Entry<K,V> {
final K key; // 键值对的键
V value; // 键值对的值
Entry<K,V> next; // 下一个节点
final int hash; // hash(key.hashCode())方法的返回值
/**
* Creates new entry.
*/
Entry(int h, K k, V v, Entry<K,V> n) { // Entry 的构造函数
value = v;
next = n;
key = k;
hash = h;
}
......
}
其中,
HashMap(int initialCapacity)
该构造函数意在构造一个指定初始容量和默认负载因子
// Constructs an empty HashMap with the specified initial capacity and the default load factor (0.75)
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR); // 直接调用上述构造函数
}
HashMap(int initialCapacity, float loadFactor)
该构造函数意在构造一个 指定初始容量 和 指定负载因子的空
/**
* Constructs an empty HashMap with the specified initial capacity and load factor.
*/
public HashMap(int initialCapacity, float loadFactor) {
//初始容量不能小于 0
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity);
//初始容量不能超过 2^30
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
//负载因子不能小于 0
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
// HashMap 的容量必须是2的幂次方,超过 initialCapacity 的最小 2^n
int capacity = 1;
while (capacity < initialCapacity)
capacity <<= 1;
//负载因子
this.loadFactor = loadFactor;
//设置HashMap的容量极限,当HashMap的容量达到该极限时就会进行自动扩容操作
threshold = (int)(capacity * loadFactor);
// HashMap的底层实现仍是数组,只是数组的每一项都是一条链
table = new Entry[capacity];
init();
}
HashMap(Map<? extends K, ? extends V> m)
该构造函数意在构造一个与指定
// Constructs a new HashMap with the same mappings as the specified Map.
// The HashMap is created with default load factor (0.75) and an initial capacity
// sufficient to hold the mappings in the specified Map.
public HashMap(Map<? extends K, ? extends V> m) {
// 初始容量不小于 16
this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
putAllForCreate(m);
}
HashMap 的底层数组长度为何总是2 的n 次方?
我们知道,h&(length - 1)
就相当于对
// HashMap 的容量必须是2的幂次方,超过 initialCapacity 的最小 2^n
int capacity = 1;
while (capacity < initialCapacity)
capacity <<= 1;
在上文已经提到过,
- 使用
hash() 方法用于对Key 的hashCode 进行重新计算,以防止质量低下的hashCode() 函数实现。由于hashMap 的支撑数组长度总是2 的倍数,通过右移可以使低位的数据尽量的不同,从而使Key 的hash 值的分布尽量均匀; - 使用
indexFor() 方法进行取余运算,以使Entry 对象的插入位置尽量分布均匀。
因此,总的来说,
- 不同的
hash 值发生碰撞的概率比较小,这样就会使得数据在table 数组中分布较均匀,空间利用率较高,查询速度也较快; h&(length - 1) 就相当于对length 取模,而且在速度、效率上比直接取模要快得多,即二者是等价不等效的,这是HashMap 在速度和效率上的一个优化。