配色: 字号:
java集合框架---HashMap
2016-09-14 | 阅:  转:  |  分享 
  
java集合框架---HashMap

1、介绍



HashMap简介



HashMap是一个散列表,它存储的内容是键值对(key-value)映射。

HashMap继承于AbstractMap,实现了Map、Cloneable、java.io.Serializable接口。

HashMap的实现不是同步的,这意味着它不是线程安全的。它的key、value都可以为null。此外,HashMap中的映射不是有序的。



HashMap的实例有两个参数影响其性能:“初始容量”和“加载因子”。容量是哈希表中桶的数量,初始容量只是哈希表在创建时的容量。加载因子是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行rehash操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数。

通常,默认加载因子是0.75,这是在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查询成本(在大多数HashMap类的操作中,包括get和put操作,都反映了这一点)。在设置初始容量时应该考虑到映射中所需的条目数及其加载因子,以便最大限度地减少rehash操作次数。如果初始容量大于最大条目数除以加载因子,则不会发生rehash操作。



HashMap的构造函数



HashMap共有4个构造函数,如下:



//默认构造函数。

HashMap()



//指定“容量大小”的构造函数

HashMap(intcapacity)



//指定“容量大小”和“加载因子”的构造函数

HashMap(intcapacity,floatloadFactor)



//包含“子Map”的构造函数

HashMap(Mapmap)



HashMap的API



voidclear()

Objectclone()

booleancontainsKey(Objectkey)

booleancontainsValue(Objectvalue)

Set>entrySet()

Vget(Objectkey)

booleanisEmpty()

SetkeySet()

Vput(Kkey,Vvalue)

voidputAll(Mapmap)

Vremove(Objectkey)

intsize()

Collectionvalues()



2、数据结构



HashMap继承于AbstractMap类,实现了Map接口。Map是”key-value键值对”接口,AbstractMap实现了”键值对”的通用函数接口。

HashMap是通过”拉链法”实现的哈希表。它包括几个重要的成员变量:table,size,threshold,loadFactor,modCount。

1)table是一个Entry[]数组类型,而Entry实际上就是一个单向链表。哈希表的”key-value键值对”都是存储在Entry数组中的。

2)size是HashMap的大小,它是HashMap保存的键值对的数量。

3)threshold是HashMap的阈值,用于判断是否需要调整HashMap的容量。threshold的值=”容量加载因子”,当HashMap中存储数据的数量达到threshold时,就需要将HashMap的容量加倍。

4)loadFactor就是加载因子。

5)modCount是用来实现fail-fast机制的。

3、源码分析



packagejava.util;

importjava.io.;



publicclassHashMap

extendsAbstractMap

implementsMap,Cloneable,Serializable

{



//默认的初始容量是16,必须是2的幂。

staticfinalintDEFAULT_INITIAL_CAPACITY=16;



//最大容量(必须是2的幂且小于2的30次方,传入容量过大将被这个值替换)

staticfinalintMAXIMUM_CAPACITY=1<<30;



//默认加载因子

staticfinalfloatDEFAULT_LOAD_FACTOR=0.75f;



//存储数据的Entry数组,长度是2的幂。

//HashMap是采用拉链法实现的,每一个Entry本质上是一个单向链表

transientEntry[]table;



//HashMap的大小,它是HashMap保存的键值对的数量

transientintsize;



//HashMap的阈值,用于判断是否需要调整HashMap的容量(threshold=容量加载因子)

intthreshold;



//加载因子实际大小

finalfloatloadFactor;



//HashMap被改变的次数

transientvolatileintmodCount;



//指定“容量大小”和“加载因子”的构造函数

publicHashMap(intinitialCapacity,floatloadFactor){

if(initialCapacity<0)

thrownewIllegalArgumentException("Illegalinitialcapacity:"+

initialCapacity);

//HashMap的最大容量只能是MAXIMUM_CAPACITY

if(initialCapacity>MAXIMUM_CAPACITY)

initialCapacity=MAXIMUM_CAPACITY;

if(loadFactor<=0||Float.isNaN(loadFactor))

thrownewIllegalArgumentException("Illegalloadfactor:"+

loadFactor);



//找出“大于initialCapacity”的最小的2的幂

intcapacity=1;

while(capacity
capacity<<=1;



//设置“加载因子”

this.loadFactor=loadFactor;

//设置“HashMap阈值”,当HashMap中存储数据的数量达到threshold时,就需要将HashMap的容量加倍。

threshold=(int)(capacityloadFactor);

//创建Entry数组,用来保存数据

table=newEntry[capacity];

init();

}





//指定“容量大小”的构造函数

publicHashMap(intinitialCapacity){

this(initialCapacity,DEFAULT_LOAD_FACTOR);

}



//默认构造函数。

publicHashMap(){

//设置“加载因子”

this.loadFactor=DEFAULT_LOAD_FACTOR;

//设置“HashMap阈值”,当HashMap中存储数据的数量达到threshold时,就需要将HashMap的容量加倍。

threshold=(int)(DEFAULT_INITIAL_CAPACITYDEFAULT_LOAD_FACTOR);

//创建Entry数组,用来保存数据

table=newEntry[DEFAULT_INITIAL_CAPACITY];

init();

}



//包含“子Map”的构造函数

publicHashMap(Mapm){

this(Math.max((int)(m.size()/DEFAULT_LOAD_FACTOR)+1,

DEFAULT_INITIAL_CAPACITY),DEFAULT_LOAD_FACTOR);

//将m中的全部元素逐个添加到HashMap中

putAllForCreate(m);

}



staticinthash(inth){

h^=(h>>>20)^(h>>>12);

returnh^(h>>>7)^(h>>>4);

}



//返回索引值

//h&(length-1)保证返回值的小于length

staticintindexFor(inth,intlength){

returnh&(length-1);

}



publicintsize(){

returnsize;

}



publicbooleanisEmpty(){

returnsize==0;

}



//获取key对应的value

publicVget(Objectkey){

if(key==null)

returngetForNullKey();

//获取key的hash值

inthash=hash(key.hashCode());

//在“该hash值对应的链表”上查找“键值等于key”的元素

for(Entrye=table[indexFor(hash,table.length)];

e!=null;

e=e.next){

Objectk;

if(e.hash==hash&&((k=e.key)==key||key.equals(k)))

returne.value;

}

returnnull;

}



//获取“key为null”的元素的值

//HashMap将“key为null”的元素存储在table[0]位置!

privateVgetForNullKey(){

for(Entrye=table[0];e!=null;e=e.next){

if(e.key==null)

returne.value;

}

returnnull;

}



//HashMap是否包含key

publicbooleancontainsKey(Objectkey){

returngetEntry(key)!=null;

}



//返回“键为key”的键值对

finalEntrygetEntry(Objectkey){

//获取哈希值

//HashMap将“key为null”的元素存储在table[0]位置,“key不为null”的则调用hash()计算哈希值

inthash=(key==null)?0:hash(key.hashCode());

//在“该hash值对应的链表”上查找“键值等于key”的元素

for(Entrye=table[indexFor(hash,table.length)];

e!=null;

e=e.next){

Objectk;

if(e.hash==hash&&

((k=e.key)==key||(key!=null&&key.equals(k))))

returne;

}

returnnull;

}



//将“key-value”添加到HashMap中

publicVput(Kkey,Vvalue){

//若“key为null”,则将该键值对添加到table[0]中。

if(key==null)

returnputForNullKey(value);

//若“key不为null”,则计算该key的哈希值,然后将其添加到该哈希值对应的链表中。

inthash=hash(key.hashCode());

inti=indexFor(hash,table.length);

for(Entrye=table[i];e!=null;e=e.next){

Objectk;

//若“该key”对应的键值对已经存在,则用新的value取代旧的value。然后退出!

if(e.hash==hash&&((k=e.key)==key||key.equals(k))){

VoldValue=e.value;

e.value=value;

e.recordAccess(this);

returnoldValue;

}

}



//若“该key”对应的键值对不存在,则将“key-value”添加到table中

modCount++;

addEntry(hash,key,value,i);

returnnull;

}



//putForNullKey()的作用是将“key为null”键值对添加到table[0]位置

privateVputForNullKey(Vvalue){

for(Entrye=table[0];e!=null;e=e.next){

if(e.key==null){

VoldValue=e.value;

e.value=value;

e.recordAccess(this);

returnoldValue;

}

}

//这里的完全不会被执行到!

modCount++;

addEntry(0,null,value,0);

returnnull;

}



//创建HashMap对应的“添加方法”,

//它和put()不同。putForCreate()是内部方法,它被构造函数等调用,用来创建HashMap

//而put()是对外提供的往HashMap中添加元素的方法。

privatevoidputForCreate(Kkey,Vvalue){

inthash=(key==null)?0:hash(key.hashCode());

inti=indexFor(hash,table.length);



//若该HashMap表中存在“键值等于key”的元素,则替换该元素的value值

for(Entrye=table[i];e!=null;e=e.next){

Objectk;

if(e.hash==hash&&

((k=e.key)==key||(key!=null&&key.equals(k)))){

e.value=value;

return;

}

}



//若该HashMap表中不存在“键值等于key”的元素,则将该key-value添加到HashMap中

createEntry(hash,key,value,i);

}



//将“m”中的全部元素都添加到HashMap中。

//该方法被内部的构造HashMap的方法所调用。

privatevoidputAllForCreate(Mapm){

//利用迭代器将元素逐个添加到HashMap中

for(Iterator>i=m.entrySet().iterator();i.hasNext();){

Map.Entrye=i.next();

putForCreate(e.getKey(),e.getValue());

}

}



//重新调整HashMap的大小,newCapacity是调整后的单位

voidresize(intnewCapacity){

Entry[]oldTable=table;

intoldCapacity=oldTable.length;

if(oldCapacity==MAXIMUM_CAPACITY){

threshold=Integer.MAX_VALUE;

return;

}



//新建一个HashMap,将“旧HashMap”的全部元素添加到“新HashMap”中,

//然后,将“新HashMap”赋值给“旧HashMap”。

Entry[]newTable=newEntry[newCapacity];

transfer(newTable);

table=newTable;

threshold=(int)(newCapacityloadFactor);

}



//将HashMap中的全部元素都添加到newTable中

voidtransfer(Entry[]newTable){

Entry[]src=table;

intnewCapacity=newTable.length;

for(intj=0;j
Entrye=src[j];

if(e!=null){

src[j]=null;

do{

Entrynext=e.next;

inti=indexFor(e.hash,newCapacity);

e.next=newTable[i];

newTable[i]=e;

e=next;

}while(e!=null);

}

}

}



//将"m"的全部元素都添加到HashMap中

publicvoidputAll(Mapm){

//有效性判断

intnumKeysToBeAdded=m.size();

if(numKeysToBeAdded==0)

return;



//计算容量是否足够,

//若“当前实际容量<需要的容量”,则将容量x2。

if(numKeysToBeAdded>threshold){

inttargetCapacity=(int)(numKeysToBeAdded/loadFactor+1);

if(targetCapacity>MAXIMUM_CAPACITY)

targetCapacity=MAXIMUM_CAPACITY;

intnewCapacity=table.length;

while(newCapacity
newCapacity<<=1;

if(newCapacity>table.length)

resize(newCapacity);

}



//通过迭代器,将“m”中的元素逐个添加到HashMap中。

for(Iterator>i=m.entrySet().iterator();i.hasNext();){

Map.Entrye=i.next();

put(e.getKey(),e.getValue());

}

}



//删除“键为key”元素

publicVremove(Objectkey){

Entrye=removeEntryForKey(key);

return(e==null?null:e.value);

}



//删除“键为key”的元素

finalEntryremoveEntryForKey(Objectkey){

//获取哈希值。若key为null,则哈希值为0;否则调用hash()进行计算

inthash=(key==null)?0:hash(key.hashCode());

inti=indexFor(hash,table.length);

Entryprev=table[i];

Entrye=prev;



//删除链表中“键为key”的元素

//本质是“删除单向链表中的节点”

while(e!=null){

Entrynext=e.next;

Objectk;

if(e.hash==hash&&

((k=e.key)==key||(key!=null&&key.equals(k)))){

modCount++;

size--;

if(prev==e)

table[i]=next;

else

prev.next=next;

e.recordRemoval(this);

returne;

}

prev=e;

e=next;

}



returne;

}



//删除“键值对”

finalEntryremoveMapping(Objecto){

if(!(oinstanceofMap.Entry))

returnnull;



Map.Entryentry=(Map.Entry)o;

Objectkey=entry.getKey();

inthash=(key==null)?0:hash(key.hashCode());

inti=indexFor(hash,table.length);

Entryprev=table[i];

Entrye=prev;



//删除链表中的“键值对e”

//本质是“删除单向链表中的节点”

while(e!=null){

Entrynext=e.next;

if(e.hash==hash&&e.equals(entry)){

modCount++;

size--;

if(prev==e)

table[i]=next;

else

prev.next=next;

e.recordRemoval(this);

returne;

}

prev=e;

e=next;

}



returne;

}



//清空HashMap,将所有的元素设为null

publicvoidclear(){

modCount++;

Entry[]tab=table;

for(inti=0;i
tab[i]=null;

size=0;

}



//是否包含“值为value”的元素

publicbooleancontainsValue(Objectvalue){

//若“value为null”,则调用containsNullValue()查找

if(value==null)

returncontainsNullValue();



//若“value不为null”,则查找HashMap中是否有值为value的节点。

Entry[]tab=table;

for(inti=0;i
for(Entrye=tab[i];e!=null;e=e.next)

if(value.equals(e.value))

returntrue;

returnfalse;

}



//是否包含null值

privatebooleancontainsNullValue(){

Entry[]tab=table;

for(inti=0;i
for(Entrye=tab[i];e!=null;e=e.next)

if(e.value==null)

returntrue;

returnfalse;

}



//克隆一个HashMap,并返回Object对象

publicObjectclone(){

HashMapresult=null;

try{

result=(HashMap)super.clone();

}catch(CloneNotSupportedExceptione){

//assertfalse;

}

result.table=newEntry[table.length];

result.entrySet=null;

result.modCount=0;

result.size=0;

result.init();

//调用putAllForCreate()将全部元素添加到HashMap中

result.putAllForCreate(this);



returnresult;

}



//Entry是单向链表。

//它是“HashMap链式存储法”对应的链表。

//它实现了Map.Entry接口,即实现getKey(),getValue(),setValue(Vvalue),equals(Objecto),hashCode()这些函数

staticclassEntryimplementsMap.Entry{

finalKkey;

Vvalue;

//指向下一个节点

Entrynext;

finalinthash;



//构造函数。

//输入参数包括"哈希值(h)","键(k)","值(v)","下一节点(n)"

Entry(inth,Kk,Vv,Entryn){

value=v;

next=n;

key=k;

hash=h;

}



publicfinalKgetKey(){

returnkey;

}



publicfinalVgetValue(){

returnvalue;

}



publicfinalVsetValue(VnewValue){

VoldValue=value;

value=newValue;

returnoldValue;

}



//判断两个Entry是否相等

//若两个Entry的“key”和“value”都相等,则返回true。

//否则,返回false

publicfinalbooleanequals(Objecto){

if(!(oinstanceofMap.Entry))

returnfalse;

Map.Entrye=(Map.Entry)o;

Objectk1=getKey();

Objectk2=e.getKey();

if(k1==k2||(k1!=null&&k1.equals(k2))){

Objectv1=getValue();

Objectv2=e.getValue();

if(v1==v2||(v1!=null&&v1.equals(v2)))

returntrue;

}

returnfalse;

}



//实现hashCode()

publicfinalinthashCode(){

return(key==null?0:key.hashCode())^

(value==null?0:value.hashCode());

}



publicfinalStringtoString(){

returngetKey()+"="+getValue();

}



//当向HashMap中添加元素时,绘调用recordAccess()。

//这里不做任何处理

voidrecordAccess(HashMapm){

}



//当从HashMap中删除元素时,绘调用recordRemoval()。

//这里不做任何处理

voidrecordRemoval(HashMapm){

}

}



//新增Entry。将“key-value”插入指定位置,bucketIndex是位置索引。

voidaddEntry(inthash,Kkey,Vvalue,intbucketIndex){

//保存“bucketIndex”位置的值到“e”中

Entrye=table[bucketIndex];

//设置“bucketIndex”位置的元素为“新Entry”,

//设置“e”为“新Entry的下一个节点”

table[bucketIndex]=newEntry(hash,key,value,e);

//若HashMap的实际大小不小于“阈值”,则调整HashMap的大小

if(size++>=threshold)

resize(2table.length);

}



//创建Entry。将“key-value”插入指定位置,bucketIndex是位置索引。

//它和addEntry的区别是:

//(01)addEntry()一般用在新增Entry可能导致“HashMap的实际容量”超过“阈值”的情况下。

//例如,我们新建一个HashMap,然后不断通过put()向HashMap中添加元素;

//put()是通过addEntry()新增Entry的。

//在这种情况下,我们不知道何时“HashMap的实际容量”会超过“阈值”;

//因此,需要调用addEntry()

//(02)createEntry()一般用在新增Entry不会导致“HashMap的实际容量”超过“阈值”的情况下。

//例如,我们调用HashMap“带有Map”的构造函数,它绘将Map的全部元素添加到HashMap中;

//但在添加之前,我们已经计算好“HashMap的容量和阈值”。也就是,可以确定“即使将Map中

//的全部元素添加到HashMap中,都不会超过HashMap的阈值”。

//此时,调用createEntry()即可。

voidcreateEntry(inthash,Kkey,Vvalue,intbucketIndex){

//保存“bucketIndex”位置的值到“e”中

Entrye=table[bucketIndex];

//设置“bucketIndex”位置的元素为“新Entry”,

//设置“e”为“新Entry的下一个节点”

table[bucketIndex]=newEntry(hash,key,value,e);

size++;

}



//HashIterator是HashMap迭代器的抽象出来的父类,实现了公共了函数。

//它包含“key迭代器(KeyIterator)”、“Value迭代器(ValueIterator)”和“Entry迭代器(EntryIterator)”3个子类。

privateabstractclassHashIteratorimplementsIterator{

//下一个元素

Entrynext;

//expectedModCount用于实现fast-fail机制。

intexpectedModCount;

//当前索引

intindex;

//当前元素

Entrycurrent;



HashIterator(){

expectedModCount=modCount;

if(size>0){//advancetofirstentry

Entry[]t=table;

//将next指向table中第一个不为null的元素。

//这里利用了index的初始值为0,从0开始依次向后遍历,直到找到不为null的元素就退出循环。

while(index
;

}

}



publicfinalbooleanhasNext(){

returnnext!=null;

}



//获取下一个元素

finalEntrynextEntry(){

if(modCount!=expectedModCount)

thrownewConcurrentModificationException();

Entrye=next;

if(e==null)

thrownewNoSuchElementException();



//注意!!!

//一个Entry就是一个单向链表

//若该Entry的下一个节点不为空,就将next指向下一个节点;

//否则,将next指向下一个链表(也是下一个Entry)的不为null的节点。

if((next=e.next)==null){

Entry[]t=table;

while(index
;

}

current=e;

returne;

}



//删除当前元素

publicvoidremove(){

if(current==null)

thrownewIllegalStateException();

if(modCount!=expectedModCount)

thrownewConcurrentModificationException();

Objectk=current.key;

current=null;

HashMap.this.removeEntryForKey(k);

expectedModCount=modCount;

}



}



//value的迭代器

privatefinalclassValueIteratorextendsHashIterator{

publicVnext(){

returnnextEntry().value;

}

}



//key的迭代器

privatefinalclassKeyIteratorextendsHashIterator{

publicKnext(){

returnnextEntry().getKey();

}

}



//Entry的迭代器

privatefinalclassEntryIteratorextendsHashIterator>{

publicMap.Entrynext(){

returnnextEntry();

}

}



//返回一个“key迭代器”

IteratornewKeyIterator(){

returnnewKeyIterator();

}

//返回一个“value迭代器”

IteratornewValueIterator(){

returnnewValueIterator();

}

//返回一个“entry迭代器”

Iterator>newEntryIterator(){

returnnewEntryIterator();

}



//HashMap的Entry对应的集合

privatetransientSet>entrySet=null;



//返回“key的集合”,实际上返回一个“KeySet对象”

publicSetkeySet(){

Setks=keySet;

return(ks!=null?ks:(keySet=newKeySet()));

}



//Key对应的集合

//KeySet继承于AbstractSet,说明该集合中没有重复的Key。

privatefinalclassKeySetextendsAbstractSet{

publicIteratoriterator(){

returnnewKeyIterator();

}

publicintsize(){

returnsize;

}

publicbooleancontains(Objecto){

returncontainsKey(o);

}

publicbooleanremove(Objecto){

returnHashMap.this.removeEntryForKey(o)!=null;

}

publicvoidclear(){

HashMap.this.clear();

}

}



//返回“value集合”,实际上返回的是一个Values对象

publicCollectionvalues(){

Collectionvs=values;

return(vs!=null?vs:(values=newValues()));

}



//“value集合”

//Values继承于AbstractCollection,不同于“KeySet继承于AbstractSet”,

//Values中的元素能够重复。因为不同的key可以指向相同的value。

privatefinalclassValuesextendsAbstractCollection{

publicIteratoriterator(){

returnnewValueIterator();

}

publicintsize(){

returnsize;

}

publicbooleancontains(Objecto){

returncontainsValue(o);

}

publicvoidclear(){

HashMap.this.clear();

}

}



//返回“HashMap的Entry集合”

publicSet>entrySet(){

returnentrySet0();

}



//返回“HashMap的Entry集合”,它实际是返回一个EntrySet对象

privateSet>entrySet0(){

Set>es=entrySet;

returnes!=null?es:(entrySet=newEntrySet());

}



//EntrySet对应的集合

//EntrySet继承于AbstractSet,说明该集合中没有重复的EntrySet。

privatefinalclassEntrySetextendsAbstractSet>{

publicIterator>iterator(){

returnnewEntwww.shanxiwang.netryIterator();

}

publicbooleancontains(Objecto){

if(!(oinstanceofMap.Entry))

returnfalse;

Map.Entrye=(Map.Entry)o;

Entrycandidate=getEntry(e.getKey());

returncandidate!=null&&candidate.equals(e);

}

publicbooleanremove(Objecto){

returnremoveMapping(o)!=null;

}

publicintsize(){

returnsize;

}

publicvoidclear(){

HashMap.this.clear();

}

}



//java.io.Serializable的写入函数

//将HashMap的“总的容量,实际容量,所有的Entry”都写入到输出流中

privatevoidwriteObject(java.io.ObjectOutputStreams)

throwsIOException

{

Iterator>i=

(size>0)?entrySet0().iterator():null;



//Writeoutthethreshold,loadfactor,andanyhiddenstuff

s.defaultWriteObject();



//Writeoutnumberofbuckets

s.writeInt(table.length);



//Writeoutsize(numberofMappings)

s.writeInt(size);



//Writeoutkeysandvalues(alternating)

if(i!=null){

while(i.hasNext()){

Map.Entrye=i.next();

s.writeObject(e.getKey());

s.writeObject(e.getValue());

}

}

}





privatestaticfinallongserialVersionUID=362498820763181265L;



//java.io.Serializable的读取函数:根据写入方式读出

//将HashMap的“总的容量,实际容量,所有的Entry”依次读出

privatevoidreadObject(java.io.ObjectInputStreams)

throwsIOException,ClassNotFoundException

{

//Readinthethreshold,loadfactor,andanyhiddenstuff

s.defaultReadObject();



//Readinnumberofbucketsandallocatethebucketarray;

intnumBuckets=s.readInt();

table=newEntry[numBuckets];



init();//Givesubclassachancetodoitsthing.



//Readinsize(numberofMappings)

intsize=s.readInt();



//Readthekeysandvalues,andputthemappingsintheHashMap

for(inti=0;i
Kkey=(K)s.readObject();

Vvalue=(V)s.readObject();

putForCreate(key,value);

}

}



//返回“HashMap总的容量”

intcapacity(){returntable.length;}

//返回“HashMap的加载因子”

floatloadFactor(){returnloadFactor;}

}



HashMap就是一个散列表,它是通过“拉链法”解决哈希冲突的。

影响HashMap性能的有两个参数:初始容量(initialCapacity)和加载因子(loadFactor)。容量

是哈希表中桶的数量,初始容量只是哈希表在创建时的容量。加载因子

是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行rehash

操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数。

HashMap拉链法分析



HashMap数据存储数组

transientEntry[]table;

HashMap中的key-value都是存储在Entry数组中的。



数据节点Entry的数据结构



staticclassEntryimplementsMap.Entry{

finalKkey;

Vvalue;

//指向下一个节点

Entrynext;

finalinthash;



//构造函数。

//输入参数包括"哈希值(h)","键(k)","值(v)","下一节点(n)"

Entry(inth,Kk,Vv,Entryn){

value=v;

next=n;

key=k;

hash=h;

}



publicfinalKgetKey(){

returnkey;

}



publicfinalVgetValue(){

returnvalue;

}



publicfinalVsetValue(VnewValue){

VoldValue=value;

value=newValue;

returnoldValue;

}



//判断两个Entry是否相等

//若两个Entry的“key”和“value”都相等,则返回true。

//否则,返回false

publicfinalbooleanequals(Objecto){

if(!(oinstanceofMap.Entry))

returnfalse;

Map.Entrye=(Map.Entry)o;

Objectk1=getKey();

Objectk2=e.getKey();

if(k1==k2||(k1!=null&&k1.equals(k2))){

Objectv1=getValue();

Objectv2=e.getValue();

if(v1==v2||(v1!=null&&v1.equals(v2)))

returntrue;

}

returnfalse;

}



//实现hashCode()

publicfinalinthashCode(){

return(key==null?0:key.hashCode())^

(value==null?0:value.hashCode());

}



publicfinalStringtoString(){

returngetKey()+"="+getValue();

}



//当向HashMap中添加元素时,绘调用recordAccess()。

//这里不做任何处理

voidrecordAccess(HashMapm){

}



//当从HashMap中删除元素时,绘调用recordRemoval()。

//这里不做任何处理

voidrecordRemoval(HashMapm){

}

}



从中,我们可以看出Entry实际上就是一个单向链表。这也是为什么我们说HashMap是通过拉链法解决哈希冲突的。

Entry实现了Map.Entry接口,即实现getKey(),getValue(),setValue(Vvalue),equals(Objecto),hashCode()这些函数。这些都是基本的读取/修改key、value值的函数。



主要的对外接口



1)clear方法

clear()的作用是清空HashMap。它是通过将所有的元素设为null来实现的。



publicvoidclear(){

modCount++;

Entry[]tab=table;

for(inti=0;i
tab[i]=null;

size=0;

}



2)containsKey()

containsKey()的作用是判断HashMap是否包含key。



publicbooleancontainsKey(Objectkey){

returngetEntry(key)!=null;

}



containsKey()首先通过getEntry(key)获取key对应的Entry,然后判断该Entry是否为null。



getEntry()源码:



finalEntrygetEntry(Objectkey){

//获取哈希值

//HashMap将“key为null”的元素存储在table[0]位置,“key不为null”的则调用hash()计算哈希值

inthash=(key==null)?0:hash(key.hashCode());

//在“该hash值对应的链表”上查找“键值等于key”的元素

for(Entrye=table[indexFor(hash,table.length)];

e!=null;

e=e.next){

Objectk;

if(e.hash==hash&&

((k=e.key)==key||(key!=null&&key.equals(k))))

returne;

}

returnnull;

}



getEntry()的作用就是返回“键为key”的键值对,它的实现源码中已经进行了说明。

这里需要强调的是:HashMap将“key为null”的元素都放在table的位置0处,即table[0]中;“key不为null”的放在table的其余位置!



3)containsValue()

containsValue()的作用是判断HashMap是否包含“值为value”的元素。



publicbooleancontainsValue(Objectvalue){

//若“value为null”,则调用containsNullValue()查找

if(value==null)

returncontainsNullValue();



//若“value不为null”,则查找HashMap中是否有值为value的节点。

Entry[]tab=table;

for(inti=0;i
for(Entrye=tab[i];e!=null;e=e.next)

if(value.equals(e.value))

returntrue;

returnfalse;

}



从中,我们可以看出containsNullValue()分为两步进行处理:第一,若“value为null”,则调用containsNullValue()。第二,若“value不为null”,则查找HashMap中是否有值为value的节点。



containsNullValue()的作用判断HashMap中是否包含“值为null”的元素。



privatebooleancontainsNullValue(){

Entry[]tab=table;

for(inti=0;i
for(Entrye=tab[i];e!=null;e=e.next)

if(e.value==null)

returntrue;

returnfalse;

}



4)entrySet()、values()、keySet()

它们3个的原理类似,这里以entrySet()为例来说明。

entrySet()的作用是返回“HashMap中所有Entry的集合”,它是一个集合。实现代码如下:



//返回“HashMap的Entry集合”

publicSet>entrySet(){

returnentrySet0();

}



//返回“HashMap的Entry集合”,它实际是返回一个EntrySet对象

privateSet>entrySet0(){

Set>es=entrySet;

returnes!=null?es:(entrySet=newEntrySet());

}



//EntrySet对应的集合

//EntrySet继承于AbstractSet,说明该集合中没有重复的EntrySet。

privatefinalclassEntrySetextendsAbstractSet>{

publicIterator>iterator(){

returnnewEntryIterator();

}

publicbooleancontains(Objecto){

if(!(oinstanceofMap.Entry))

returnfalse;

Map.Entrye=(Map.Entry)o;

Entrycandidate=getEntry(e.getKey());

returncandidate!=null&&candidate.equals(e);

}

publicbooleanremove(Objecto){

returnremoveMapping(o)!=null;

}

publicintsize(){

returnsize;

}

publicvoidclear(){

HashMap.this.clear();

}

}



HashMap是通过拉链法实现的散列表。表现在HashMap包括许多的Entry,而每一个Entry本质上又是一个单向链表。那么HashMap遍历key-value键值对的时候,是如何逐个去遍历的呢?



下面我们就看看HashMap是如何通过entrySet()遍历的。

entrySet()实际上是通过newEntryIterator()实现的。下面我们看看它的代码:



//返回一个“entry迭代器”

Iterator>newEntryIterator(){

returnnewEntryIterator();

}



//Entry的迭代器

privatefinalclassEntryIteratorextendsHashIterator>{

publicMap.Entrynext(){

returnnextEntry();

}

}



//HashIterator是HashMap迭代器的抽象出来的父类,实现了公共了函数。

//它包含“key迭代器(KeyIterator)”、“Value迭代器(ValueIterator)”和“Entry迭代器(EntryIterator)”3个子类。

privateabstractclassHashIteratorimplementsIterator{

//下一个元素

Entrynext;

//expectedModCount用于实现fast-fail机制。

intexpectedModCount;

//当前索引

intindex;

//当前元素

Entrycurrent;



HashIterator(){

expectedModCount=modCount;

if(size>0){//advancetofirstentry

Entry[]t=table;

//将next指向table中第一个不为null的元素。

//这里利用了index的初始值为0,从0开始依次向后遍历,直到找到不为null的元素就退出循环。

while(index
;

}

}



publicfinalbooleanhasNext(){

returnnext!=null;

}



//获取下一个元素

finalEntrynextEntry(){

if(modCount!=expectedModCount)

thrownewConcurrentModificationException();

Entrye=next;

if(e==null)

thrownewNoSuchElementException();



//注意!!!

//一个Entry就是一个单向链表

//若该Entry的下一个节点不为空,就将next指向下一个节点;

//否则,将next指向下一个链表(也是下一个Entry)的不为null的节点。

if((next=e.next)==null){

Entry[]t=table;

while(index
;

}

current=e;

returne;

}



//删除当前元素

publicvoidremove(){

if(current==null)

thrownewIllegalStateException();

if(modCount!=expectedModCount)

thrownewConcurrentModificationException();

Objectk=current.key;

current=null;

HashMap.this.removeEntryForKey(k);

expectedModCount=modCount;

}



}



当我们通过entrySet()获取到的Iterator的next()方法去遍历HashMap时,实际上调用的是nextEntry()。而nextEntry()的实现方式,先遍历Entry(根据Entry在table中的序号,从小到大的遍历);然后对每个Entry(即每个单向链表),逐个遍历。



5)get()

get()的作用是获取key对应的value,它的实现代码如下:



publicVget(Objectkey){

if(key==null)

returngetForNullKey();

//获取key的hash值

inthash=hash(key.hashCode());

//在“该hash值对应的链表”上查找“键值等于key”的元素

for(Entrye=table[indexFor(hash,table.length)];

e!=null;

e=e.next){

Objectk;

if(e.hash==hash&&((k=e.key)==key||key.equals(k)))

returne.value;

}

returnnull;

}



6)put()



put()的作用是对外提供接口,让HashMap对象可以通过put()将“key-value”添加到HashMap中。



publicVput(Kkey,Vvalue){

//若“key为null”,则将该键值对添加到table[0]中。

if(key==null)

returnputForNullKey(value);

//若“key不为null”,则计算该key的哈希值,然后将其添加到该哈希值对应的链表中。

inthash=hash(key.hashCode());

inti=indexFor(hash,table.length);

for(Entrye=table[i];e!=null;e=e.next){

Objectk;

//若“该key”对应的键值对已经存在,则用新的value取代旧的value。然后退出!

if(e.hash==hash&&((k=e.key)==key||key.equals(k))){

VoldValue=e.value;

e.value=value;

e.recordAccess(this);

returnoldValue;

}

}



//若“该key”对应的键值对不存在,则将“key-value”添加到table中

modCount++;

addEntry(hash,key,value,i);

returnnull;

}



若要添加到HashMap中的键值对对应的key已经存在HashMap中,则找到该键值对;然后新的value取代旧的value,并退出!

若要添加到HashMap中的键值对对应的key不在HashMap中,则将其添加到该哈希值对应的链表中,并调用addEntry()。



下面看看addEntry()的代码:



voidaddEntry(inthash,Kkey,Vvalue,intbucketIndex){

//保存“bucketIndex”位置的值到“e”中

Entrye=table[bucketIndex];

//设置“bucketIndex”位置的元素为“新Entry”,

//设置“e”为“新Entry的下一个节点”

table[bucketIndex]=newEntry(hash,key,value,e);

//若HashMap的实际大小不小于“阈值”,则调整HashMap的大小

if(size++>=threshold)

resize(2table.length);

}



addEntry()的作用是新增Entry。将“key-value”插入指定位置,bucketIndex是位置索引。



createEntry()的代码如下:



voidcreateEntry(inthash,Kkey,Vvalue,intbucketIndex){

//保存“bucketIndex”位置的值到“e”中

Entrye=table[bucketIndex];

//设置“bucketIndex”位置的元素为“新Entry”,

//设置“e”为“新Entry的下一个节点”

table[bucketIndex]=newEntry(hash,key,value,e);

size++;

}



比较addEntry()和createEntry()的代码,我们发现addEntry()多了两句:



if(size++>=threshold)

resize(2table.length);

1

2

那它们的区别到底是什么呢?

阅读代码,我们可以发现,它们的使用情景不同。

(01)addEntry()一般用在新增Entry可能导致“HashMap的实际容量”超过“阈值”的情况下。

例如,我们新建一个HashMap,然后不断通过put()向HashMap中添加元素;put()是通过addEntry()新增Entry的。

在这种情况下,我们不知道何时“HashMap的实际容量”会超过“阈值”;

因此,需要调用addEntry()

(02)createEntry()一般用在新增Entry不会导致“HashMap的实际容量”超过“阈值”的情况下。

例如,我们调用HashMap“带有Map”的构造函数,它绘将Map的全部元素添加到HashMap中;

但在添加之前,我们已经计算好“HashMap的容量和阈值”。也就是,可以确定“即使将Map中的全部元素添加到HashMap中,都不会超过HashMap的阈值”。

此时,调用createEntry()即可。

7)putAll



8)remove



4、遍历方式



遍历HashMap的键值对



第一步:根据entrySet()获取HashMap的“键值对”的Set集合。

第二步:通过Iterator迭代器遍历“第一步”得到的集合。



//假设map是HashMap对象

//map中的key是String类型,value是Integer类型

Integerinteg=null;

Iteratoriter=map.entrySet().iterator();

while(iter.hasNext()){

Map.Entryentry=(Map.Entry)iter.next();

//获取key

key=(String)entry.getKey();

//获取value

integ=(Integer)entry.getValue();

}



遍历HashMap的键



第一步:根据keySet()获取HashMap的“键”的Set集合。

第二步:通过Iterator迭代器遍历“第一步”得到的集合。



//假设map是HashMap对象

//map中的key是String类型,value是Integer类型

Stringkey=null;

Integerinteg=null;

Iteratoriter=map.keySet().iterator();

while(iter.hasNext()){

//获取key

key=(String)iter.next();

//根据key,获取value

integ=(Integer)map.get(key);

}



遍历HashMap的值



第一步:根据value()获取HashMap的“值”的集合。

第二步:通过Iterator迭代器遍历“第一步”得到的集合。



//假设map是HashMap对象

//map中的key是String类型,value是Integer类型

Integervalue=null;

Collectionc=map.values();

Iteratoriter=c.iterator();

while(iter.hasNext()){

value=(Integer)iter.next();

}

献花(0)
+1
(本文系网络学习天...首藏)