Java集合深入学习 - HashMap源码解析-1基础(基于jdk1.8)
Java集合深入学习 - HashMap源码解析-2查找数据(基于jdk1.8)
Java集合深入学习 - HashMap源码解析-3添加与扩容(基于jdk1.8)
Java集合深入学习 - HashMap源码解析-4删改与遍历(基于jdk1.8)
1.添加数据put
/**
* 添加数据
*/
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
/**
*
* @param hash key的hash值
* @param key
* @param value
* @param onlyIfAbsent 是否不允许覆盖value
* @param evict
* @return
*/
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0) //Map还未初始化的情况
n = (tab = resize()).length; //初始化Map resize之后再说
if ((p = tab[i = (n - 1) & hash]) == null) //根据key的hash值计算数据位置,数据位置无数据的情况
tab[i] = newNode(hash, key, value, null); //直接生成一个新的节点放入数组中
else { //该位置已存在数据
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k)))) //比较hash值和equals判断是否是当前位置
e = p; //记录当前节点
else if (p instanceof TreeNode) //是红黑树节点
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); //获取到对应的红黑树节点
else { //当前位置是单链表节点
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) { //当前节点是最后一个节点
p.next = newNode(hash, key, value, null); //生成一个新的节点放到链表最后
if (binCount >= TREEIFY_THRESHOLD - 1) // 当前链表长度(新节点加入前的长度)>= TREEIFY_THRESHOLD - 1
treeifyBin(tab, hash); //将链表转换成二叉树 若此时数组大小小于阀值 会进行扩容,而不会将链表转换成二叉树
break; //跳出循环
}
if (e.hash == hash && //查找到对应已存在的节点
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e; //遍历
}
}
if (e != null) { //当前key在Map中已存在
V oldValue = e.value; //获取原来的value值
if (!onlyIfAbsent || oldValue == null) //是否可以覆盖value
e.value = value; //覆盖原来的额value值
afterNodeAccess(e); //为继承HashMap的LinkedHashMap类服务的方法调用
return oldValue; //返回原来的value值
}
}
++modCount; //操作次数+1
if (++size > threshold) //判断是否需要对Map进行扩容
resize(); //扩容
afterNodeInsertion(evict); //为继承HashMap的LinkedHashMap类服务的方法调用
return null;
}
/**
* 将数组中hash值对应位置的链表转换成红黑树
*/
final void treeifyBin(Node<K,V>[] tab, int hash) {
int n, index; Node<K,V> e;
if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY) //数组长度 小于 最小树形化容量阈值
resize(); //直接对Map进行扩容
else if ((e = tab[index = (n - 1) & hash]) != null) { //数组中对应位置的链表存在
TreeNode<K,V> hd = null, tl = null;
do {
TreeNode<K,V> p = replacementTreeNode(e, null); //根据链表节点数据生成红黑树节点
if (tl == null)
hd = p;
else { //整理成双向链表的结构,记录其前后节点
p.prev = tl;
tl.next = p;
}
tl = p;
} while ((e = e.next) != null); //遍历当前链表的所有数据
if ((tab[index] = hd) != null) //转换后数组中对应节点有数据
hd.treeify(tab); //整理红黑树结构
}
}
2.不覆盖值得添加数据putVal
/**
* 添加数据,若对应key已存在,并且对应value非空,返回对应value 否则添加数据并返回当前value
*/
@Override
public V putIfAbsent(K key, V value) {
return putVal(hash(key), key, value, true, true);
}
3.批量添加
/**
* 批量添加数据
*/
public void putAll(Map<? extends K, ? extends V> m) {
putMapEntries(m, true); //如果有key对应value相同且非空 不进行覆盖操作
}
4.HashMap的扩容处理resize
/**
* 对map进行扩容或者初始化
*/
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table; //原数组
int oldCap = (oldTab == null) ? 0 : oldTab.length; //原数组大小
int oldThr = threshold; //原扩容阀值
int newCap, newThr = 0; //定义新的
if (oldCap > 0) {
if (oldCap >= MAXIMUM_CAPACITY) { //大于最大值 不进行扩容
threshold = Integer.MAX_VALUE;
return oldTab;
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY) //扩容不会大于最大值 原大小不会小于默认的最小值
newThr = oldThr << 1; //扩容为当前大小的2倍
}
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr; //计算新的数组大小
else { // 初始化,按默认值计算
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;
//扩容后数据处理
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
if (oldTab != null) { //原数组非空
for (int j = 0; j < oldCap; ++j) { //遍历原数组
Node<K,V> e;
if ((e = oldTab[j]) != null) { //获取数组当前位置数据 (当前位置存在数据)
oldTab[j] = null; //清空原数组中当前位置数据
if (e.next == null) //当前位置只有一个数据
newTab[e.hash & (newCap - 1)] = e; //重新计算数据存放到新数组中
else if (e instanceof TreeNode) //当前位置是红黑树的数据结构
((TreeNode<K,V>)e).split(this, newTab, j, oldCap); //调用节点的split方法处理数据
else { // 当前位置是链表的数据结构
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) { //该节点是否需要移动((e.hash & oldCap) == 0 不需要移动)
if (loTail == null) //第一个满足条件 初始化loHead
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; //返回新的数组
}
/**
* 红黑树节点split方法处理数据
* @param map 原Map对象
* @param tab 新数组
* @param index 当前遍历位置
* @param bit 原数组大小
*/
final void split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) {
TreeNode<K,V> b = this; //获取当前节点
TreeNode<K,V> loHead = null, loTail = null; //loHead和loTail两个节点分别记录不需要移动的链表的头部和尾部
TreeNode<K,V> hiHead = null, hiTail = null; //hiHead和hiTail分别记录需要移动的链表头部和尾部
int lc = 0, hc = 0; //统计需要移动和不需要移动节点个数
for (TreeNode<K,V> e = b, next; e != null; e = next) { //获取当前节点并遍历
next = (TreeNode<K,V>)e.next; //获取下一个节点
e.next = null; //当前节点置空
/**
* bit是原数组大小 最高位是1 其余位都是0
* 与e.hash进行按位与操作之后得出的结果是0或者bit
*/
if ((e.hash & bit) == 0) { //判断节点是否需要移动
if ((e.prev = loTail) == null) //判断loTail是否为空,即判断当前节点是否为第一个节点
loHead = e; //设置当前节点为不需要西东节点
else //不是第一个节点
loTail.next = e; //将当前节点放到loTail之后
loTail = e; //将当前loTail设置为当前节点
++lc; //不需要移动节点个数+1
}
else { //同上处理
if ((e.prev = hiTail) == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
++hc;
}
}
if (loHead != null) { //存在有不需要移动的数据
if (lc <= UNTREEIFY_THRESHOLD) //判断是否需要将二叉树转换成链表
tab[index] = loHead.untreeify(map); //数据处理拼接单向链表
else { //不需要转换成链表
tab[index] = loHead; //放入数据
if (hiHead != null) //节点非空
loHead.treeify(tab); //整理树结构
}
}
if (hiHead != null) { //存在需要移动的数据
if (hc <= UNTREEIFY_THRESHOLD) //是否需要转换成链表
tab[index + bit] = hiHead.untreeify(map); //数据处理成单链表
else {
tab[index + bit] = hiHead; //放入数据
if (loHead != null) //节点非空
hiHead.treeify(tab); //整理树结构
}
}
}
/**
* 节点转换成链表结构
*/
final Node<K,V> untreeify(HashMap<K,V> map) {
Node<K,V> hd = null, tl = null;
for (Node<K,V> q = this; q != null; q = q.next) { //遍历当前链
Node<K,V> p = map.replacementNode(q, null); //生成新节点并将下一个节点置空 (单链表)
/**
* 第一次进入循环后 hd=t1=p
* hd记录头节点信息 t1用来拼接链表
*/
if (tl == null)
hd = p; //当前节点
else
tl.next = p; //t1的下一个节点设置为当前节点
tl = p; //tl设置为当前节点(向后移动)
}
return hd;
}
本文链接,转载请标注https://blog.csdn.net/luo_mu_hpu/article/details/106230620