前言
HashMap是面试中比较常见的问题,这一篇,我们将通过阅读源码,了解其设计原理以及以下问题
- HashMap的实现原理
- 初始容量为什么是2的倍数
- 如何resize
- 是否线程安全
常用参数
//最大容量 2的30次方
static final int MAXIMUM_CAPACITY = 1 << 30;
//初始容量为16 扩容时才会触发
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
//默认的加载因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//哈希表,存放链表。 长度是2的N次方,或者初始化时为0.
transient Node<K,V>[] table;
//加载因子,用于计算哈希表元素数量的阈值。 threshold = 哈希表.length * loadFactor;
final float loadFactor;
//哈希表内元素数量的阈值,当哈希表内元素数量超过阈值时,会发生扩容resize()。
int threshold;
其中,table称之为哈希表,用于存放 链表Node
Node 链表
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V 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;
}
}
可以看到,Node的数据结构是Node嵌套Node,故称之为链表; 整个链表中的某个Node称之为节点。
构造方法
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
public HashMap(int initialCapacity) {
//指定初始化容量的构造函数
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
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;
//设置阈值 初始化容量的 2的n次方
this.threshold = tableSizeFor(initialCapacity);
}
public HashMap(Map&l