一、介绍
-
基本概念
HashMap其实就是数据结构的哈希表,是一个为了快速查找定义的数据结构,旨在提高在一个数组中查找任意一个元素的时间复杂度。存储数据时给定(Key,Value),通过hash()函数得到hash作为表数组(table)的下标,查询时直接通过table[hash(key)]即可查到value,大幅提高查询效率而且这与数组元素的是否有序无关
-
哈希冲突(哈希碰撞)
理想情况下是通过改造hash函数可以一次查询到想要的value,然而这必定是会有两个不同的key求出的hash值是一样的情况,那么table[hash(key)]就有两个元素,这是不允许的,这种情况称为哈希冲突
-
解决哈希冲突
解决哈希冲突的方法有很多种,而HashMap使用的是链地址法(拉链法)【事实上JDK1.8使用的是链表和红黑树相互转化】,将产生冲突的两个value用链表连接起来,而他们的hash值是一样的,table数组每一个下标对应着一条链表,一条链表称为一个桶(bucket),一个桶中有一个或多个结点
-
装载因子(HashMap中称为负载因子)
负载因子满足 s i z e ( 已 有 元 素 的 总 容 量 ) c a p a c i t y ( t a b l e 数 组 长 度 ) = l o a d f a c t o r y ( 负 载 因 子 ) \frac{size(已有元素的总容量)}{capacity(table数组长度)}=loadfactory(负载因子) capacity(table数组长度)size(已有元素的总容量)=loadfactory(负载因子)可以看出
负载因子越大元素在table数组中更为密集,这就导致计算hash值时更容易产生哈希冲突;
负载因子越小,数组需要的空间就越大造成空间浪费,这是一种空间换时间的解决方案
二、源码流程分析
友情提示:注释为个人理解,原英文注释可自行翻看源码,有大段解释文字在代码注释上哦,手机访问时点击代码可横屏显示,观看更舒适(若发现错误可私聊或评论矫正)。 |
1、首先认识这个类的相关变量
// 哈希表,即上文说的table数组,容量必须为2的指数次方,后面会介绍原因
transient Node<K,V>[] table;
/**
* Holds cached entrySet(). Note that AbstractMap fields are used
* for keySet() and values().
*/
transient Set<Map.Entry<K,V>> entrySet;
// 所有的(key,value)对总数量大小
transient int size;
// 要调整大小的下一个大小值(容量*负载系数),size大于它则进行扩容
int threshold;
// 负载因子
final float loadFactor;
// 默认初始化容量为16,容量必须为2的指数次方
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
// 由于容量为2的指数次方,故最大容量为2的30次方(由于int二进制为32位且最高位为符号位)
static final int MAXIMUM_CAPACITY = 1 << 30;
// 默认负载因子大小为0.75,f表示为浮点数,这个大小是经过计算和实际检验的合适值
static final float DEFAULT_LOAD_FACTOR = 0.75f;
// 树化阈值,当一个桶中的结点数 > 8时将会把桶的链表形式转化为红黑树
static final int TREEIFY_THRESHOLD = 8;
// 解除树化的阈值,当一个桶中的结点数 < 6时会把桶的红黑树形式转化为链表
static final int UNTREEIFY_THRESHOLD = 6;
// 最小树化容量,当容量<这个值,即使达到了TREEIFY_THRESHOLD也不转化为红黑树而是进行扩容
static final int MIN_TREEIFY_CAPACITY = 64;
静态内部类Node结点分析
static class Node<K,V> implements Map.Entry<K,V> {
// 计算的hash值
final int hash;
// 分别是设置的key和value值
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; }
// 重写hashcode方法使node结点返回地址利用到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;
}
// 重写equals方法,如果对象o的引用等于该node返回true
// 如果o是Map的结点类型且key和value都相等也返回true
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.