HashMap 是一线资深 java工程师必须要精通的集合容器,它的重要性几乎等同于Volatile在并发编程的重要性(可见性与有序性)。
本篇通过图文源码详解,深度剖析 HashMap 的重要内核知识,易看易学易懂。建议收藏,多学一点总是好的,万一面试被问到了呢。
我是Mike,10余年BAT一线大厂架构技术倾囊相授。本篇重点:
- HashMap的数据结构
- HashMap核心成员
- HashMapd的Node数组
- HashMap的数据存储
- HashMap的哈希函数
- 哈希冲突:链式哈希表
- HashMap的get方法:哈希函数
- HashMap的put方法
- 为什么槽位数必须使用2^n?
HashMap的数据结构
首先,我们从数据结构的角度来看:HashMap是:数组+链表+红黑树(JDK1.8增加了红黑树部分)的数据结构,如下所示:
这里需要搞明白两个问题:
数据底层具体存储的是什么?
这样的存储方式有什么优点呢?
默认初始容量(数组默认大小):16,2的整数次方
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
最大容量
static final int MAXIMUM_CAPACITY = 1 << 30;
默认负载因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;
装载因子用来衡量HashMap满的程度,表示当map集合中存储的数据达到当前数组大小的75%则需要进行扩容
链表转红黑树边界
static final int TREEIFY_THRESHOLD = 8;
红黑树转离链表边界
static final int UNTREEIFY_THRESHOLD = 6;
哈希桶数组
transient Node<K,V>[] table;
实际存储的元素个数
transient int size;
当map里面的数据大于这个threshold就会进行扩容
int threshold 阈值 = table.length * loadFactor
2.Node数组
从源码可知,HashMap类中有一个非常重要的字段,就是 Node[] table,即哈希桶数组,明显它是一个Node的数组。
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;//用来定位数组索引位置
final K key;
V value;
Node<K,V> next;//链表的下一个Node节点
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) {