深刻懂得Java中的HashMap的完成機制。本站提示廣大學習愛好者:(深刻懂得Java中的HashMap的完成機制)文章只能為提供參考,不一定能成為您想要的結果。以下是深刻懂得Java中的HashMap的完成機制正文
假如任何人讓我描寫一下HashMap的任務機制的話,我就簡略的答復:“基於Hash的規矩”。這句話異常簡略,然則要懂得這句話之前,起首我們得懂得甚麼是哈希,不是麼?
甚麼是哈希
哈希簡略的說就是對變量/對象的屬性運用某種算法後獲得的一個獨一的串,用這個串來肯定變量/對象的獨一性。一個准確的哈希函數必需遵照這個原則。
當哈希函數運用在雷同的對象或許equal的對象的時刻,每次履行都應當前往雷同的值。換句話說,兩個相等的對象應當有雷同的hashcode。
注:一切Java對象都從Object類繼續了一個默許的hashCode()辦法。這個辦法將對象在內存中的地址作為整數前往,這是一個很好的hash完成,他確保了分歧的對象具有分歧的hashcode。
關於Entry類的一點引見
一個map的界說是:一個映照鍵(key)到值(value)的對象。異常簡略對吧。
所以,在HashMap中必定有必定的機制來存儲這些鍵值對。使得,HashMap有一個 外部類Entry,看起來像如許。
static class Entry<K,V> implements Map.Entry<K,V> { final K key; V value; Entry<K,V> next; final int hash; ...//More code goes here }
固然,Entry類有屬性用來存儲鍵值對映照。key被final標志,除key和value ,我們還能看到兩個變量next和hash。接上去我們試著懂得這些變量的寄義。
put()辦法現實上做了甚麼
再進一步看put辦法的完成之前,我們有需要看一看Entry實例在數組中的存儲, HashMap中是如許界說的:
/** * The table, resized as necessary. Length MUST Always be a power of two. */ transient Entry[] table; 如今再來看put辦法的完成。 /** * Associates the specified value with the specified key in this map. * If the map previously contained a mapping for the key, the old * value is replaced. * * @param key key with which the specified value is to be associated * @param value value to be associated with the specified key * @return the previous value associated with <tt>key</tt>, or * <tt>null</tt> if there was no mapping for <tt>key</tt>. * (A <tt>null</tt> return can also indicate that the map * previously associated <tt>null</tt> with <tt>key</tt>.) */ public V put(K key, V value) { if (key == null) return putForNullKey(value); int hash = hash(key.hashCode()); int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(hash, key, value, i); return null; }
讓我們一步一步的看
起首,檢討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.
/** * Returns the value to which the specified key is mapped, * or {@code null} if this map contains no mapping for the key. * * <p>More formally, if this map contains a mapping from a key * {@code k} to a value {@code v} such that {@code (key==null ? k==null : * key.equals(k))}, then this method returns {@code v}; otherwise * it returns {@code null}. (There can be at most one such mapping.) * * <p>A return value of {@code null} does not <i>necessarily</i> * indicate that the map contains no mapping for the key; it's also * possible that the map explicitly maps the key to {@code null}. * The {@link #containsKey containsKey} operation may be used to * distinguish these two cases. * * @see #put(Object, Object) */ public V get(Object key) { if (key == null) return getForNullKey(); int hash = hash(key.hashCode()); for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) return e.value; } return null; }
下面的代碼看起來跟put()辦法很像,除if (e.hash == hash && ((k = e.key) == key || key.equals(k)))。
留意點