之前有写到ArrayList的源码分析,欢迎大家点开我的头像查看

对于HashMap这个类大家一定不陌生,想必多多少少用过或者了解过,今天我来和大家谈谈HashMap的源码,JDK为1.8

继承AbstractMap类,实现map接口等等

HashMap源码分析(一)_HashMap源码分析

当你不设置它的容量时默认的容量大小  为16

HashMap源码分析(一)_初始化_02

这个属性代表着它的最大容量,大小可以理解为2的30次方

HashMap源码分析(一)_HashMap_03

负载因子,如果构造方法没有指定负载因子的话默认0.75,也就是当它容量达到百分之75的时候扩容

HashMap源码分析(一)_初始化_04

这个属性代表着如果它的链表结构长度达到了8的时候链表的数据结构将会变成红黑树,提高效率

HashMap源码分析(一)_HashMap源码分析_05

如果链表结构长度小于6的时候又会从红黑树变成链表,因为当你长度就这么长的时候链表性能反而更好

HashMap源码分析(一)_HashMap源码分析_06

容器可以树化的最小表容量,可以理解为当它变成红黑树的最小表容量

HashMap源码分析(一)_赋值_07

在第一次使用的时候初始化

HashMap源码分析(一)_初始化_08

保存缓存的set映射 遍历map的时候需要

HashMap源码分析(一)_赋值_09

长度,不多说

HashMap源码分析(一)_链表_10

代表它被修改的次数

HashMap源码分析(一)_HashMap源码分析_11

常用的属性就介绍到这里,接下来我们来详细看看HashMap是怎么样put添加数据的

put方法直接是返回putVal,那我们详细看看这个方法

HashMap源码分析(一)_HashMap源码分析_12

注释写在代码中

/**
*put方法的内部
* Implements Map.put and related methods
* @param hash hash for key
* @param key the key
* @param value the value to put
* @param onlyIfAbsent if true, don't change existing value
* @param evict if false, the table is in creation mode.
* @return previous value, or null if none
*/
final V putVal(int hash, K key, V value, boolean onlyIfAbsent false, boolean evict true) {

Node<K,V>[] tab; Node<K,V> p; int n, i;
//如果是空,也就代表是第一次put ,
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length; //生成初始化的长度
if ((p = tab[i = (n - 1) & hash]) == null) //如果值==null,则证明这个数据为null
tab[i] = newNode(hash, key, value, null); //hash为key通过hash方法的到 将tab的第0个值设置为null
else { //反之则不为空时
Node<K,V> e; K k; //创建一个node<>和 一个key类型的对象 k
if (p.hash == hash && //如果p的hash和key的hash一致并且p的key==k或者参数key不为空并且key equals k 则将p赋给e
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode) //如果p是TreeNode类型的则进入 putTreeVal方法
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) { //如果p.next为空,则新建一个node
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st //如果循环次数>=变成红黑树的要求数
treeifyBin(tab, hash); //调整表的大小
break;
}
if (e.hash == hash && //相同hash和key则停止遍历
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e; //将e赋值给p
}
}
if (e != null) { //如果e!=null,则表示map中的键有和put的键重复的,将会返回旧数据的value
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount; //更改次数+1
if (++size > threshold) //长度+1,如果长度大于需要扩容的大小时 resize初始化或加倍表格大小
resize();
afterNodeInsertion(evict); //节点插入后的一个回调方法
return null;
}
}

其中这个方法又调用了resize()方法

//初始化或加倍表格大小。如果为null,则分配符合字段阈值中保存的初始容量目标。 
//否则,因为我们正在使用2次幂扩展,所以每个bin中的元素必须保持在相同的索引处,
//或者在新表中以2的偏移量移动。

final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;//table为map初始化的node
int oldCap = (oldTab == null) ? 0 : oldTab.length; //如果为空则返回0,否则返回oldTab的长度
int oldThr = threshold;//threshold表示到达该数map将会扩容 例如刚开始新建为16*0.75
int newCap, newThr = 0;//newThr为要达到扩容的数 newCap为map的大小容量
if (oldCap > 0) { //如果map初始化的node大于0
if (oldCap >= MAXIMUM_CAPACITY) { //如果大于或者等于最大允许容量 1<<30
threshold = Integer.MAX_VALUE; //threshold 等于int的最大数值
return oldTab; //将map初始化的node返回
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && //如果 newCap==0或者大小正常 再将oldCap*2赋给newCap,判断这个newCap是否小于1<<30并且大于默认长度16
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold //newThr则为下一个调整扩容的2倍
}
else if (oldThr > 0) // initial capacity was placed in threshold //如果到达扩容数大于0,newCap得到到达扩容数的值
newCap = oldThr;
else { //零初始阈值表示使用默认值
newCap = DEFAULT_INITIAL_CAPACITY; //newCap为默认map大小16
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); //newThr为默认将要到达则需要扩容的数
}
if (newThr == 0) { //如果将要扩容数newThr为0,
float ft = (float)newCap * loadFactor; //ft为map新容量*哈希表的加载因子 默认是0.75,但可以new对象时可以手动设置
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE); //当map新容量小于1<<30时并且ft小于1<<30时
返回ft不然为int最大值 newThr为将要达到扩容时数值 (第一次为12)
}
threshold = newThr; //将newThr赋值给达到将要扩容的数
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap]; //new一个容量为新大小的tab
table = newTab; //将新大小的tab赋值给 一个抑制的table
if (oldTab != null) { //如果旧tab不是空
for (int j = 0; j < oldCap; ++j) { //遍历旧tab
Node<K,V> e;
if ((e = oldTab[j]) != null) { //当前node不为空
oldTab[j] = null; //赋值为空
if (e.next == null) //如果下一个为空
newTab[e.hash & (newCap - 1)] = e; //&代表如果前面的为false后面的将不会执行
else if (e instanceof TreeNode)
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else { // preserve order
Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
next = e.next;
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}

HashMap主要是通过这两个方法进行put操作