Java中HashMap的工作机制

欢迎进入Java社区论坛,与200万技术人员互动交流 >>进入

put()方法实际上做了什么

再进一步看put方法的实现之前,我们有必要看一看Entry实例在数组中的存储,HashMap中是这样定义的:

/** *Thetable,resizedasnecessary.LengthMUSTAlwaysbeapoweroftwo. */transientEntry[]table;

现在再来看put方法的实现。

/** *Associatesthespecifiedvaluewiththespecifiedkeyinthismap. *Ifthemappreviouslycontainedamappingforthekey,theold *valueisreplaced. * *@paramkeykeywithwhichthespecifiedvalueistobeassociated *@paramvaluevaluetobeassociatedwiththespecifiedkey *@returnthepreviousvalueassociatedwith<tt>key</tt>,or *&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<tt>null</tt>iftherewasnomappingfor<tt>key</tt>. *&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(A<tt>null</tt>returncanalsoindicatethatthemap *&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;previouslyassociated<tt>null</tt>with<tt>key</tt>.) */publicVput(Kkey,Vvalue){ if(key==null) returnputForNullKey(value); inthash=hash(key.hashCode()); inti=indexFor(hash,table.length); for(Entry<K,V>e=table[i];e!=null;e=e.next){ Objectk; if(e.hash==hash&&((k=e.key)==key||key.equals(k))){ VoldValue=e.value; e.value=value; e.recordAccess(this); returnoldValue; } } modCount++; addEntry(hash,key,value,i); returnnull; }

让我们一步一步的看

首先,检查key是否为null,如果key是null值被存在table[0]的位置,因为null的hashcode始终为0接下来,通过key的hashCode()方法计算了这个key的hash值,这个hash值被用来计算存储Entry对象的数组中的位置。JDK的设计者假设会有一些人可能写出非常差的hashCode()方法,会出现一些非常大或者非常小的hash值。为了解决这个问题,他们引入了另外一个hash函数,接受对象的hashCode(),并转换到适合数组的容量大小。

接着是indexFor(hash,table,length)方法,这个方法计算了entry对象存储的准确位置。

接下来就是主要的部分,我们都知道两个不相等的对象可能拥有过相同的hashCode值,两个不同的对象是怎么存储在相同的位置[叫做bucket]呢?

答案是LinkedList。如果你记得,Entry类有一个next变量,这个变量总是指向链中的下一个变量,这完全符合链表的特点。

所以,在发生碰撞的时候,entry对象会被以链表的形式存储起来,当一个Entry对象需要被存储的时候,hashmap检查该位置是否已近有了一个entry对象,如果没有就存在那里,如果有了就检查她的next属性,如果是空,当前的entry对象就作为已经存储的entry对象的下一个节点,依次类推。

如果我们给已经存在的key存入另一个value会怎么样的?逻辑上,旧的值将被替换掉。在检测了Entry对象的存储位置后,hashmap将会遍历那个位置的entry链表,对每一个entry调用equals方法,这个链表中的所有对象都具有相同的hashCode()而equals方法都不等。如果发现equals方法有相等的就执行替换。

在这种方式下HashMap就能保证key的唯一性。

get方法的工作机制

现在我们已经了解了HashMap中存储键值对的机制。下一个问题是:怎样从一个HashMap中查询结果。

其实逻辑跟put是一样的,如果传入的key有匹配就将该位置的value返回,如果没有就返回null.

/** *Returnsthevaluetowhichthespecifiedkeyismapped, *or{@codenull}ifthismapcontainsnomappingforthekey. * *<p>Moreformally,ifthismapcontainsamappingfromakey *{@codek}toavalue{@codev}suchthat{@code(key==null?k==null: *key.equals(k))},thenthismethodreturns{@codev};otherwise *itreturns{@codenull}.&nbsp;(Therecanbeatmostonesuchmapping.) * *<p>Areturnvalueof{@codenull}doesnot<i>necessarily</i> *indicatethatthemapcontainsnomappingforthekey;it’salso *possiblethatthemapexplicitlymapsthekeyto{@codenull}. *The{@link#containsKeycontainsKey}operationmaybeusedto *distinguishthesetwocases. * *@see#put(Object,Object) */publicVget(Objectkey){ if(key==null) returngetForNullKey(); inthash=hash(key.hashCode()); for(Entry<K,V>e=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; }

上面的代码看起来跟put()方法很像,除了if (e.hash == hash && ((k = e.key) == key || key.equals(k)))。

注意点

存储Entry对象的数据结构是一个叫做Entry类型的table数组。

数组中一个特定的索引位置称为bucket,因为它可以容纳一个LinkedList的第一个元素的对象。

Key对象的hashCode()需要用来计算Entry对象的存储位置。

Key对象的equals()方法需要用来维持Map中对象的唯一性。

get()和put()方法跟Value对象的hashCode和equals方法无关。

null的hashCode总是0,这样的Entry对象总是被存储在数组的第一个位置

[1][2]

即使没有收获的希望也心平气和的继续。

Java中HashMap的工作机制

相关文章:

你感兴趣的文章:

标签云: