HashMap源码学习

简介: 线程上:HashMap是线程不安全的,而HashTable是安全的。key、value的支持:HashMap的key、balue可以为空,而HashTable是key、value不可以为空的。底层数据结构:HashMap采用数组+链表+红黑树,当链表的长度>=8的时候会考虑是否转成红黑树,而HashTable则没有。初始化容量上:HashTable的初始化容量是11,同时扩容变为原来的2n+1,HashMap的初始化容量是16,同时扩容扩容成原来的2倍。而当给定初始化容量时,HashTable是直接给定初始化容量,而HashMap是将给定的初始化容量变成2的次幂。

首先实现map的子类:HashMap、HashTable、TreeMap、LinkedHashMap。

这几个类中HashMap与HashTable的区别:

线程上:HashMap是线程不安全的,而HashTable是安全的。key、value的支持:HashMap的key、balue可以为空,而HashTable是key、value不可以为空的。底层数据结构:HashMap采用数组+链表+红黑树,当链表的长度>=8的时候会考虑是否转成红黑树,而HashTable则没有。初始化容量上:HashTable的初始化容量是11,同时扩容变为原来的2n+1,HashMap的初始化容量是16,同时扩容扩容成原来的2倍。而当给定初始化容量时,HashTable是直接给定初始化容量,而HashMap是将给定的初始化容量变成2的次幂。

jdk7和jdk8之间,jdk8引入了红黑树,同时jdk7中的transform( )方法变成了resize( )方法。什么是红黑树,同时数组什么时候会加入链表,链表和数组满足什么条件时,链表会变成红黑树,什么时候红黑树又会变成链表。同时如果给定初始化容量,给成怎样会才合理。


一、HashMap相关变量

//默认初始化容量2^4 =16,做左移位运算staticfinalintDEFAULT_INITIAL_CAPACITY=1<<4;
//最大容量 2^30staticfinalintMAXIMUM_CAPACITY=1<<30;
//默认加载因子 0.75staticfinalfloatDEFAULT_LOAD_FACTOR=0.75f;
//需要变成红黑树的链表阀值 8staticfinalintTREEIFY_THRESHOLD=8;
//将红黑树变成链表的阀值 6staticfinalintUNTREEIFY_THRESHOLD=6;
//变成红黑树除了满足链表达到8,而且数组需要//达到64,最小转成红黑树的数组长度64staticfinalintMIN_TREEIFY_CAPACITY=64;
//数组,又称为bucket,桶,2的次幂transientNode<K,V>[] table;
//entrySet:k、v对的集合transientSet<Map.Entry<K,V>>entrySet;
//map的长度transientintsize;
//修改次数transientintmodCount;
//阀值  threshold = capacity*loadFactorintthreshold;
//加载因子finalfloatloadFactor;


二、构造函数

//构造函数,对传入的加载因子和初始化容量校验publicHashMap(intinitialCapacity,floatloadFactor){//如果初始容量<0,则抛异常if (initialCapacity<0)
thrownewIllegalArgumentException(
"Illegal initial capacity: "+initialCapacity);
//如果初始容量>最大容量,则将最大容量赋值给初始化容量if (initialCapacity>MAXIMUM_CAPACITY)
initialCapacity=MAXIMUM_CAPACITY;
//加载因子如果小于0或者加载因子为空,则抛异常if (loadFactor<=0||Float.isNaN(loadFactor))
thrownewIllegalArgumentException(
"Illegal load factor: "+loadFactor);
this.loadFactor=loadFactor;
this.threshold=tableSizeFor(initialCapacity);
}
//将传入的容量变成2的n次幂staticfinalinttableSizeFor(intcap) {
//向减去1,比如避免16变成32intn=cap-1;
n|=n>>>1;
n|=n>>>2;
n|=n>>>4;
n|=n>>>8;
n|=n>>>16;
//之后判断如果大于最大值,变成最大值,//否者变成n+1,也即变成2的次幂的形式return (n<0) ?1 : (
n>=MAXIMUM_CAPACITY) ?MAXIMUM_CAPACITY:n+1;
}
//构造hashMap,传入初始化容量,默认的加载因子publicHashMap(intinitialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
//构造hash,加载因子默认,容量默认publicHashMap() {
this.loadFactor=DEFAULT_LOAD_FACTOR;
}
//构造一个HashMap:加载因子为默认加载因子publicHashMap(Map<?extendsK, ?extendsV>m) {
this.loadFactor=DEFAULT_LOAD_FACTOR;
//放入传入的map信息、putMapEntries(m, false);
}
finalvoidputMapEntries(Map<?extendsK, ?extendsV>m, booleanevict) {
//拿到map为m的长度ints=m.size();
//如果长度>0,则判断是否为空if (s>0) {
if (table==null) { // pre-size//拿到ft=长度/加载因子+1floatft= ((float)s/loadFactor)+1.0F;
//如果ft<最大容量,则为ft合理的长度intt= ((ft< (float)MAXIMUM_CAPACITY) ?                   (int)ft : MAXIMUM_CAPACITY);
//如果t>阀值,则需要进行初始化,变成2的次幂,// 类似于扩容,考虑到为变成16的情况if (t>threshold)
threshold=tableSizeFor(t);
         }
//否者,s>阀值,则会进行扩容操作elseif (s>threshold)
resize();
//拿到key、value的值,执行put操作for (Map.Entry<?extendsK, ?extendsV>e : m.entrySet()) {
Kkey=e.getKey();
Vvalue=e.getValue();
//进行put操作,添加元素信息putVal(hash(key), key, value,false,evict);
            }
        }
    }


三、方法

put方法

publicVput(Kkey, Vvalue) {
returnputVal(hash(key),
key, value, false, true);
}
//注意到onlyIfAbsent为false,则可以进行值的替换,// 如果设置为true,则不可以进行修改value的值finalVputVal(inthash, Kkey, Vvalue,
booleanonlyIfAbsent,booleanevict){
//定义变量Node<K,V>[] tab; Node<K,V>p; intn, i;
//如果table(数组)为空,则进行resize()进行初始化if ((tab=table) ==null|| (n=tab.length)==0)
n= (tab=resize()).length;
//(n-1)&hash,相当于对其进行取模,放入到哪个数组中if ((p=tab[i= (n-1) &hash]) ==null)
//放入到数组中tab[i] =newNode(hash, key, value, null);
//如果不为空else {
Node<K,V>e; Kk;
//如果key存在同时hash值相同,//也即数组中已经有元素存在时,//则更新value的值if (p.hash==hash&&    ((k=p.key)==key||(key!=null&&key.equals(k))))
e=p;
//如果元素属于树节点,则进行红黑树的判断,执//行红黑树操作elseif (pinstanceofTreeNode)
e=((TreeNode<K,V>)p).putTreeVal(this,tab, 
hash, key, value);
//否者执行链表操作,元素在链表中else {
//遍历链表for (intbinCount=0; ; ++binCount){
//遍历到链表尾部if ((e=p.next) ==null) {
//为元素创建信息节点p.next=newNode(hash, key, value, null);
//如果binCount个数,也即//链表个数>=8时,则进行红黑树操作if (binCount>=TREEIFY_THRESHOLD-1)
//执行红黑树操作,首先会去判断数组的// 长度是否>=64,执行完进行断开treeifyBin(tab, hash);
break;
                }
//如果待插入的key在链表中找到,则退出循环if (e.hash==hash&&                 ((k=e.key) ==key|| (key!=null&&key.equals(k))))
break;
//与前面的e = p.next构成循环p=e;
            }
        }
//如果e存在,则将新值替换旧值if (e!=null) {
VoldValue=e.value;
//判断是否替换成新值if(!onlyIfAbsent||oldValue==null)
//替换旧值为新值e.value=value;
//回调函数,留给LInkedHashMap实现afterNodeAccess(e);
returnoldValue;
        }
    }
//修改次数自增++modCount;
//size>阀值,进行扩容操作if (++size>threshold)
resize();
//回调函数,留给LinkedHashMap实现afterNodeInsertion(evict);
returnnull;
}
/*** Tree version of putVal.*///红黑树的put放入方法finalTreeNode<K,V>putTreeVal(HashMap<K,V>map, 
Node<K,V>[] tab,inth, Kk, Vv){
Class<?>kc=null;
booleansearched=false;
//获取根节点TreeNode<K,V>root=(parent!=null)?root():this;
//从根节点进行遍历for (TreeNode<K,V>p=root;;) {
intdir, ph; Kpk;
//hash值下小于父节点的hash值,则取左节点if ((ph=p.hash) >h)
dir=-1;
//如果hash值大于父节点hash值,则取右节点elseif (ph<h)
dir=1;
//如果hash相等,继续比较键值是否相等,//如果相等则说明存在相同的键值,则直接返回//当前节点的值elseif((pk=p.key)==k||(k!=null&&k.equals(pk)))
returnp;
//如果hash值相等,但是key不相等elseif ((kc==null&&//key没有实现Comparable比较接口      (kc=comparableClassFor(k)) ==null) ||//或者已经实现Comparable//比较接口,同时等于0//则不能进行key的顺序判断,//因此无法判断,会产生hash碰撞    (dir=compareComparables(kc, k, pk)) ==0){
//判断是否执行过查找方法if (!searched) {
TreeNode<K,V>q, ch;
//如果执行过,则分别查找左//右树,查看是否有相同的对象,//如果找不到,则说明确实//没有找到相同对象存在searched=true;
if (((ch=p.left) !=null&&              (q=ch.find(h, k, kc)) !=null)||               ((ch=p.right) !=null&&               (q=ch.find(h, k, kc)) !=null))
returnq;
            }
//hash相同,key不相等,//并且key没有实现Comparable接口,//保持一致的处理规则,//以便进行排序,从而进行遍历dir=tieBreakOrder(k, pk);
        }
//备份当前节点TreeNode<K,V>xp=p;
//如果遍历完之后还是没有//找到相同的key的节点,则进行新的插入操作if((p=(dir<=0) ?p.left : p.right)==null){
Node<K,V>xpn=xp.next;
TreeNode<K,V>x=map.newTreeNode(h,k,v,xpn);
//备份当前节点的下一个//节点;给LinkedHashMap预留的if (dir<=0)
xp.left=x;
elsexp.right=x;
//将当前节点的next节点指向新节点xp.next=x;
//设置新的父节点和当前节点为当前节点x.parent=x.prev=xp;
//如果当前节点还是后继节点,// 则将新节点作为当前节点的前驱节点if (xpn!=null)
                ((TreeNode<K,V>)xpn).prev=x;
//实现红黑树重排实现重平衡moveRootToFront(tab, balanceInsertion(root, x));
returnnull;
        }
    }
}
//进行数组长度64的判断,如果成立,则将链表转换为红黑树finalvoidtreeifyBin(Node<K,V>[] tab, inthash) {
intn, index; Node<K,V>e;
//如果数组长度<64,则进行扩容操作if(tab==null||(n=tab.length)<MIN_TREEIFY_CAPACITY)
resize();
//否者进行红黑树操作elseif((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);
    }
}
/*** Forms tree of the nodes linked from this node.* @return root of tree*///进行重平衡操作finalvoidtreeify(Node<K,V>[] tab) {
TreeNode<K,V>root=null;
for (TreeNode<K,V>x=this, next; x!=null; x=next) {
next= (TreeNode<K,V>)x.next;
x.left=x.right=null;
if (root==null) {
x.parent=null;
x.red=false;
root=x;
        }
else {
Kk=x.key;
inth=x.hash;
Class<?>kc=null;
for (TreeNode<K,V>p=root;;) {
intdir, ph;
Kpk=p.key;
if ((ph=p.hash) >h)
dir=-1;
elseif (ph<h)
dir=1;
elseif ((kc==null&&          (kc=comparableClassFor(k))==null)||          (dir=compareComparables(kc,k,pk))==0)
dir=tieBreakOrder(k, pk);
TreeNode<K,V>xp=p;
if ((p=(dir<=0)?p.left:p.right)==null){
x.parent=xp;
if (dir<=0)
xp.left=x;
elsexp.right=x;
root=balanceInsertion(root,x);
break;
                }
            }
        }
    }
moveRootToFront(tab, root);
}
//将root节点放到哈希表的首元素static<K,V>voidmoveRootToFront(Node<K,V>[]tab,  
TreeNode<K,V>root) {
intn;
if(root!=null&&tab!=null&& (n=tab.length)>0){
intindex= (n-1) &root.hash;
TreeNode<K,V>first=(TreeNode<K,V>)tab[index];
if (root!=first) {
Node<K,V>rn;
tab[index] =root;
TreeNode<K,V>rp=root.prev;
if ((rn=root.next) !=null)
                ((TreeNode<K,V>)rn).prev=rp;
if (rp!=null)
rp.next=rn;
if (first!=null)
first.prev=root;
root.next=first;
root.prev=null;
        }
assertcheckInvariants(root);
    }
}
//树节点查找finalTreeNode<K,V>find(inth,Objectk,
Class<?>kc){
TreeNode<K,V>p=this;
//进行do-while循环找到相要的元素do {
intph, dir; Kpk;
TreeNode<K,V>pl=p.left, pr=p.right, q;
//hash值小于当前节点,则在左节点进行比较,否者在右节点比较if ((ph=p.hash) >h)
p=pl;
elseif (ph<h)
p=pr;
//否者是当前的节点,直接返回elseif((pk=p.key)==k||(k!=null&&k.equals(pk)))
returnp;
//左节点为空,则遍历右节点elseif (pl==null)
p=pr;
//右节点为空,则遍历左节点elseif (pr==null)
p=pl;
//如果实现了Comparable接口,//则更加compareTo比较出来的大小获取顺序elseif ((kc!=null||         (kc=comparableClassFor(k))!=null) &&         (dir=compareComparables(kc, k, pk))!=0)
p= (dir<0) ?pl : pr;
//如果没有则采用find()放到进行返回elseif ((q=pr.find(h, k, kc))!=null)
returnq;
elsep=pl;
    } while (p!=null);
returnnull;
}

扩容操作

//进行扩容操作finalNode<K,V>[] resize() {
//老的数组Node<K,V>[] oldTab=table;
intoldCap=(oldTab==null)?0:oldTab.length;
intoldThr=threshold;
intnewCap, newThr=0;
//首先老的容量是否大于0,如果大于0,//则进行最大容量判断,如果最大容量判断不为空,// 则将阀值设置为最大容量,返回老容量if (oldCap>0) {
if (oldCap>=MAXIMUM_CAPACITY) {
threshold=Integer.MAX_VALUE;
returnoldTab;
        }
//否者对新容量进行做位运算左移一位,扩容2倍//如果小于最大容量,同时老容量大于默认初始化容量,//则阀值扩容成原来的2倍elseif((newCap=oldCap<<1<MAXIMUM_CAPACITY&&oldCap>=DEFAULT_INITIAL_CAPACITY)
newThr=oldThr<<1; //double threshold    }
//如果老阀值大于0,则新容量等于老阀值elseif (oldThr>0)
newCap=oldThr;
else {//否者没有进行初始容量,则//采用默认容量16,新的阀值为16*0.75newCap=DEFAULT_INITIAL_CAPACITY;
newThr=(int)(DEFAULT_LOAD_FACTOR*DEFAULT_INITIAL_CAPACITY);
    }
//如果新的阀值为0,则采用默认的if (newThr==0) {
floatft= (float)newCap*loadFactor;
newThr= (newCap<MAXIMUM_CAPACITY&&ft< (float)MAXIMUM_CAPACITY?                (int)ft : Integer.MAX_VALUE);
    }
//采用新的阀值threshold=newThr;
//创建一个新的数组,同时 newCap = 0,方便扩容使用@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab=(Node<K,V>[])newNode[newCap];
//数组等于新的tabtable=newTab;
//原来的hash表不为空时,将原来的元素复制//到新的hash表中if (oldTab!=null) {
for (intj=0; j<oldCap; ++j) {
Node<K,V>e;
if ((e=oldTab[j]) !=null) {
//将原来的元素置空oldTab[j] =null;
//如果当前元素的后继为空,将e放//入到取模的新数组中if (e.next==null)
newTab[e.hash&(newCap-1)]=e;
//如果为树节点,则进行//红黑树的扩容操作elseif(einstanceofTreeNode)
                 ((TreeNode<K,V>)e).split(this                 ,newTab, j, oldCap);
//保持顺序else { // preserve order//lohead指low的低位的头//结点,loTail表示低位的尾节点Node<K,V>loHead=null, loTail=null;
//hihead指low的高位的头结点,//hiTail表示高位的尾节点Node<K,V>hiHead=null,hiTail=null;
//下一个节点Node<K,V>next;
//取模只可能出现0或者1的情况do {
//e元素的前驱节点next=e.next;
//如hash值对老容量取余为0//,则放入到低位的链表中if((e.hash&oldCap)==0){
if (loTail==null)
loHead=e;
elseloTail.next=e;
loTail=e;
                        }
//否者则说明hash值对老容量取模不为0,//也就是为1的情况,放在高位的链表中else {
if (hiTail==null)
hiHead=e;
elsehiTail.next=e;
hiTail=e;
                        }
                    } while ((e=next) !=null);
if (loTail!=null) {
//遍历完成分化两个链表//低位链表在新的哈希表//中的位置与旧哈希表一样loTail.next=null;
newTab[j] =loHead;
                    }
//高位链表在新哈希表中//的位置为原来的位置+oldcapif (hiTail!=null) {
hiTail.next=null;
newTab[j+oldCap]=hiHead;
                    }
                }
            }
        }
    }
returnnewTab;
}
//将树中的节点拆分为较高和较低的树,//如果现在太小,则取消树化。仅从调整//大小调用。也即进行树变链表和链表转成树的操作finalvoidsplit(HashMap<K,V>map,
Node<K,V>[] tab, intindex, intbit) {
//获取当前元素TreeNode<K,V>b=this;
//低位、高位的头结点和尾节点TreeNode<K,V>loHead=null, loTail=null;
TreeNode<K,V>hiHead=null, hiTail=null;
intlc=0, hc=0;
//从根节点开始遍历红黑树,//统计出需要放到低位和需要移动到高位的元素个数//进行红黑树操作或者红黑树转链表操作for (TreeNode<K,V>e=b, next; e!=null; 
e=next) {
next= (TreeNode<K,V>)e.next;
e.next=null;
if ((e.hash&bit) ==0) {
if ((e.prev=loTail) ==null)
loHead=e;
elseloTail.next=e;
loTail=e;
++lc;
        }
else {
if ((e.prev=hiTail) ==null)
hiHead=e;
elsehiTail.next=e;
hiTail=e;
++hc;
        }
    }
//如果低位头结点不为//空,则进行低位操作if (loHead!=null) {
//小于红黑树阀值时,//则不满足红黑树操作,转链表if (lc<=UNTREEIFY_THRESHOLD)
tab[index] =loHead.untreeify(map);
//否者进行红黑树操作,//将低位头结点放入到hash表中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);
        }
    }
}
//将树转成链表finalNode<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);
if (tl==null)
hd=p;
elsetl.next=p;
tl=p;
    }
returnhd;
}

remove操作

//进行删除操作publicVremove(Objectkey) {
Node<K,V>e;
return(e=removeNode(hash(key),key,null,false,
true))==null?null : e.value;
}
//删除节点信息finalNode<K,V>removeNode(inthash, Objectkey,
Objectvalue,booleanmatchValue,booleanmovable){
Node<K,V>[] tab; Node<K,V>p; intn, index;
//如果hash表、表长度不为空,//同时hash取模不为空,则进行移除操作if ((tab=table)!=null&& (n=tab.length)>0&&      (p=tab[index=(n-1) &hash])!=null) {
Node<K,V>node=null, e; Kk; Vv;
//找到该元素进行存放if (p.hash==hash&& ((k=p.key)==key||(key!=null&&key.equals(k))))
node=p;
//如果当前节点的下一个节点不//为空,则有可能为树节点,或者链表elseif ((e=p.next) !=null) {
//树节点,拿到该元素信息if (pinstanceofTreeNode)
node=(TreeNode<K,V>)p).getTreeNode(hash,key);
//链表,拿到该元素else {
do {
if (e.hash==hash&&                   ((k=e.key) ==key||                    (key!=null&&key.equals(k)))) {
node=e;
break;
                    }
p=e;
                } while ((e=e.next) !=null);
            }
        }
//拿到之后,进行删除操作if (node!=null&& (!matchValue|| (
v=node.value) ==value||           (value!=null&&value.equals(v)))){
if (nodeinstanceofTreeNode)
 ((TreeNode<K,V>)node).removeTreeNode(
this, tab,movable);
elseif (node==p)
tab[index] =node.next;
elsep.next=node.next;
++modCount;
--size;
afterNodeRemoval(node);
returnnode;
        }
    }
returnnull;
}
//树节点查找finalTreeNode<K,V>getTreeNode(inth,Objectk){
return ((parent!=null) ?root() : this).find(h, k, null);
}
//树节点查找finalTreeNode<K,V>find(inth,Objectk,
Class<?>kc){
TreeNode<K,V>p=this;
//进行do-while循环找到相要的元素do {
intph, dir; Kpk;
TreeNode<K,V>pl=p.left, pr=p.right,q;
//hash值小于当前节点,//则在左节点进行比较,否者在右节点比较if ((ph=p.hash) >h)
p=pl;
elseif (ph<h)
p=pr;
//否者是当前的节点,直接返回elseif ((pk=p.key) ==k||              (k!=null&&k.equals(pk)))
returnp;
//左节点为空,则遍历右节点elseif (pl==null)
p=pr;
//右节点为空,则遍历左节点elseif (pr==null)
p=pl;
//如果实现了Comparable接口,//则更加compareTo比较出来的大小获取顺序elseif ((kc!=null||         (kc=comparableClassFor(k))!=null) &&         (dir=compareComparables(kc, k, pk))!=0)
p= (dir<0) ?pl : pr;
//如果没有则采用find()放到进行返回elseif ((q=pr.find(h, k, kc))!=null)
returnq;
elsep=pl;
    } while (p!=null);
returnnull;
}
//进行红黑树的删除操作finalvoidremoveTreeNode(HashMap<K,V>map, 
Node<K,V>[] tab,booleanmovable){
intn;
//进行判空操作,如果为空,则直接返回if (tab==null|| (n=tab.length) ==0)
return;
//进行取余操作intindex= (n-1) &hash;
TreeNode<K,V>first=(TreeNode<K,V>)tab[index],
root=first, rl;
//下一个元素 pred = prev为前驱TreeNode<K,V>succ= (TreeNode<K,V>)next, 
pred=prev;
//前驱节点为空,则说明为头结点,执行将位//置指向后元素if (pred==null)
tab[index] =first=succ;
//否者,则说明头结点不为空,//则进行前驱节点的下一个节点指向后节点elsepred.next=succ;
//如果后节点不为空,//将后节点的前一个节点指向前节点if (succ!=null)
succ.prev=pred;
//如果first为空,则说明没有元素,直接返回if (first==null)
return;
//root还有父节点,则说明不是根,拿到根if (root.parent!=null)
root=root.root();
if (root==null||root.right==null||      (rl=root.left)==null||rl.left=null){
//红黑树转链表操作tab[index] =first.untreeify(map);
return;
    }
//p为删除节点,pl为左节点,//pr为有节点,replacement为要移动的子节点TreeNode<K,V>p=this,pl=left,
pr=right,replacement;
//左右节点 不为空if (pl!=null&&pr!=null) {
//s为右节点TreeNode<K,V>s=pr, sl;
//s的右节点的左节点不为空while ((sl=s.left) !=null) 
s=sl;
booleanc=s.red; s.red=p.red;p.red=c;
//交换删除节点和右节点的左节点的颜色,//进行变色操作TreeNode<K,V>sr=s.right;
TreeNode<K,V>pp=p.parent;
if (s==pr) {
p.parent=s;
s.right=p;
        }
else {
TreeNode<K,V>sp=s.parent;
if ((p.parent=sp) !=null) {
if (s==sp.left)
sp.left=p;
elsesp.right=p;
            }
if ((s.right=pr) !=null)
pr.parent=s;
        }
p.left=null;
if ((p.right=sr) !=null)
sr.parent=p;
if ((s.left=pl) !=null)
pl.parent=s;
if ((s.parent=pp) ==null)
root=s;
elseif (p==pp.left)
pp.left=s;
elsepp.right=s;
if (sr!=null)
replacement=sr;
elsereplacement=p;
    }
//pl不为空,则删除节点为左节点elseif (pl!=null)
replacement=pl;
//pr不为空,则删除节点为右节点elseif (pr!=null)
replacement=pr;
//删除元素为叶子节点elsereplacement=p;
//如果删除节点后进行//红黑树处理,使用变色和旋转if (replacement!=p) {
TreeNode<K,V>pp=replacement.parent=p.parent;
if (pp==null)
root=replacement;
elseif (p==pp.left)
pp.left=replacement;
elsepp.right=replacement;
p.left=p.right=p.parent=null;
    }
TreeNode<K,V>r=p.red?root:
balanceDeletion(root,replacement);
//删除节点为叶子节点,//则删除与之关联的节点if (replacement==p) {  // detachTreeNode<K,V>pp=p.parent;
p.parent=null;
if (pp!=null) {
if (p==pp.left)
pp.left=null;
elseif (p==pp.right)
pp.right=null;
        }
    }
//根据标识决定是否//将根节点转移hash表首元素if (movable)
moveRootToFront(tab, r);
}

get操作

//通过key获取value的信息publicVget(Objectkey) {
//节点信息,返回节点的值Node<K,V>e;
//获取value值return (e=getNode(hash(key), key)) 
==null?null : e.value;
}
//通过hash值和key,拿到节点//信息,进一步拿到value值finalNode<K,V>getNode(inthash, Objectkey) {
Node<K,V>[] tab;Node<K,V>first,e;intn;Kk;
//哈希表不为空,同时长度//大于0,hash取模不为空时if((tab=table) !=null&& (n=tab.length)>0&&(first=tab[(n-1) &hash]) !=null){
//进行判断首节点的hash、key是否与之相同if (first.hash==hash&&           ((k=first.key) ==key||(key!=null&&key.equals(k))))
returnfirst;
//如果首节点有后继节点,//则说明有链表或者红黑树,此时需要进行判盘//如果为树节点,则进行树节点//信息的获取,否者为链表,遍历链表获取元素if ((e=first.next) !=null) {
if (firstinstanceofTreeNode)
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
do {
if (e.hash==hash&&              ((k=e.key) ==key||(key!=null&&key.equals(k))))
returne;
            } while ((e=e.next) !=null);
        }
    }
returnnull;
}
finalTreeNode<K,V>getTreeNode(inth, Objectk){
return ((parent!=null) ?root() : 
this).find(h, k, null);
}
//树节点查找finalTreeNode<K,V>find(inth, Objectk, 
Class<?>kc) {
TreeNode<K,V>p=this;
//进行do-while循环找到相要的元素do {
intph, dir; Kpk;
TreeNode<K,V>pl=p.left, pr=p.right, q;
//hash值小于当前节点,则在//左节点进行比较,否者在右节点比较if ((ph=p.hash) >h)
p=pl;
elseif (ph<h)
p=pr;
//否者是当前的节点,直接返回elseif((pk=p.key)==k||(k!=null&&k.equals(pk)))
returnp;
//左节点为空,则遍历右节点elseif (pl==null)
p=pr;
//右节点为空,则遍历左节点elseif (pr==null)
p=pl;
//如果实现了Comparable接口,//则更加compareTo比较出来的大小获取顺序elseif ((kc!=null||         (kc=comparableClassFor(k))!=null) &&         (dir=compareComparables(kc, k, pk))!=0)
p= (dir<0) ?pl : pr;
//如果没有则采用find()放到进行返回elseif((q=pr.find(h, k, kc))!=null)
returnq;
elsep=pl;
    } while (p!=null);
returnnull;
}
//比较器staticClass<?>comparableClassFor(Objectx){
if (xinstanceofComparable) {
Class<?>c; Type[] ts, as; Typet; 
ParameterizedTypep;
if ((c=x.getClass())==String.class)
returnc;
if((ts=c.getGenericInterfaces())!=null){
for(inti=0; i<ts.length; ++i) {
if (((t=ts[i]) instanceofParameterizedType) &&                 ((p= (ParameterizedType)t).getRawType() ==Comparable.class) &&       (as=p.getActualTypeArguments())!=null&&as.length==1&&as[0] ==c)
returnc;
                }
            }
        }
returnnull;
    }
//比较器@SuppressWarnings({"rawtypes","unchecked"})
staticintcompareComparables(Class<?>kc,Objectk, Objectx) {
return (x==null||x.getClass() !=kc?0 :((Comparable)k).compareTo(x));
    }

从里面可以收获到的一些结论:

HashMap的源码较多,且综合了数组、链表、红黑树的操作,因此不管是get还是remove还是get都需要考虑到三张数据结构的操作。同时红黑树是一种平衡二叉树,节点是黑色或者红色的,根节点为黑色的,每个红色节点的两个叶子节点都是黑色的,从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。同时当key相同时,会出现冲突,此时就需要解决hash冲突,此时就将其放入到链表中。而当链表的长度>=8时,数组长度>=64时,则变成红黑树。给定初始化容量时,给定元素个数/加载因子为最佳初始化容量,可以在源码找到这个代码。

目录
相关文章
|
1月前
|
Java
让星星⭐月亮告诉你,HashMap中保证红黑树根节点一定是对应链表头节点moveRootToFront()方法源码解读
当红黑树的根节点不是其对应链表的头节点时,通过调整指针的方式将其移动至链表头部。具体步骤包括:从链表中移除根节点,更新根节点及其前后节点的指针,确保根节点成为新的头节点,并保持链表结构的完整性。此过程在Java的`HashMap$TreeNode.moveRootToFront()`方法中实现,确保了高效的数据访问与管理。
29 2
|
1月前
|
Java 索引
让星星⭐月亮告诉你,HashMap之往红黑树添加元素-putTreeVal方法源码解读
本文详细解析了Java `HashMap` 中 `putTreeVal` 方法的源码,该方法用于在红黑树中添加元素。当数组索引位置已存在红黑树类型的元素时,会调用此方法。具体步骤包括:从根节点开始遍历红黑树,找到合适位置插入新元素,调整节点指针,保持红黑树平衡,并确保根节点是链表头节点。通过源码解析,帮助读者深入理解 `HashMap` 的内部实现机制。
33 2
|
1月前
|
算法 Java 容器
Map - HashSet & HashMap 源码解析
Map - HashSet & HashMap 源码解析
52 0
|
1月前
|
存储
让星星⭐月亮告诉你,HashMap的put方法源码解析及其中两种会触发扩容的场景(足够详尽,有问题欢迎指正~)
`HashMap`的`put`方法通过调用`putVal`实现,主要涉及两个场景下的扩容操作:1. 初始化时,链表数组的初始容量设为16,阈值设为12;2. 当存储的元素个数超过阈值时,链表数组的容量和阈值均翻倍。`putVal`方法处理键值对的插入,包括链表和红黑树的转换,确保高效的数据存取。
53 5
|
1月前
|
算法 索引
让星星⭐月亮告诉你,HashMap的resize()即扩容方法源码解读(已重新完善,如有不足之处,欢迎指正~)
`HashMap`的`resize()`方法主要用于数组扩容,包括初始化或加倍数组容量。该方法首先计算新的数组容量和扩容阈值,然后创建新数组。接着,旧数组中的数据根据`(e.hash & oldCap)`是否等于0被重新分配到新数组中,分为低位区和高位区两个链表,确保数据迁移时的正确性和高效性。
64 3
|
1月前
|
Java 索引
让星星⭐月亮告诉你,HashMap中红黑树TreeNode的split方法源码解读
本文详细解析了Java中`HashMap`的`TreeNode`类的`split`方法,该方法主要用于在`HashMap`扩容时将红黑树节点从旧数组迁移到新数组,并根据`(e.hash & oldCap)`的结果将节点分为低位和高位两个子树。低位子树如果元素数少于等于6,则进行去树化操作;若多于6且高位子树非空,则进行树化操作,确保数据结构的高效性。文中还介绍了`untreeify`和`replacementNode`方法,分别用于将红黑树节点转换为普通链表节点。
23 2
|
1月前
|
存储 Java
HashMap之链表转红黑树(树化 )-treefyBin方法源码解读(所有涉及到的方法均有详细解读,欢迎指正)
本文详细解析了Java HashMap中链表转红黑树的机制,包括树化条件(链表长度达8且数组长度≥64)及转换流程,确保高效处理大量数据。
71 1
|
1月前
|
存储 缓存 Java
【用Java学习数据结构系列】HashMap与TreeMap的区别,以及Map与Set的关系
【用Java学习数据结构系列】HashMap与TreeMap的区别,以及Map与Set的关系
34 1
|
1月前
|
存储 缓存 Java
HashMap源码解析
HashMap源码解析
|
6月前
|
Java
IDEA debug HashMap源码的心得
IDEA debug HashMap源码的心得
63 0