咱們都知道數組和鏈表的優劣,那HashMap有效地整合了數組和鏈表,造成了新的一種數據裝載模型。數組
利用數組+鏈表的優點this
一、減小數組沒必要要空間的開闢(假如單純利用數組實現裝載,咱們總要考慮預先分配一部份內存,總不能須要的時候纔開闢吧?在設計理念上也不符),利用鏈表可以更好地實現動態分配內存(經過引用關係指定);spa
二、經過散列均勻地將下標一致的對象掛在相應的鏈表下,這樣的好處是經過數組和鏈表的整合減小了查詢的複雜度(遍歷最大次數爲 數組長度+某鏈表下長度)設計
三、總之一句話「悟天克斯的合體」code
HashMap最基本的結構是由Entry 一個內部靜態類定義的對象
static class Entry<K,V> implements Map.Entry<K,V> { final K key;//key V value;//value Entry<K,V> next;//鏈表結構,指定的下一個Entry int hash;//經過散列獲取的惟一的hash值,在put環節將做爲一部分條件來驗證Entry是否重複 /** * Creates new entry. */ Entry(int h, K k, V v, Entry<K,V> n) { value = v; next = n; key = k; hash = h; } public final K getKey() { return key; } public final V getValue() { return value; } public final V setValue(V newValue) { V oldValue = value; value = newValue; return oldValue; } public final boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry e = (Map.Entry)o; Object k1 = getKey(); Object k2 = e.getKey(); if (k1 == k2 || (k1 != null && k1.equals(k2))) { Object v1 = getValue(); Object v2 = e.getValue(); if (v1 == v2 || (v1 != null && v1.equals(v2))) return true; } return false; } public final int hashCode() { return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue()); } public final String toString() { return getKey() + "=" + getValue(); } /** * This method is invoked whenever the value in an entry is * overwritten by an invocation of put(k,v) for a key k that's already * in the HashMap. */ void recordAccess(HashMap<K,V> m) { } /** * This method is invoked whenever the entry is * removed from the table. */ void recordRemoval(HashMap<K,V> m) { } }
如下是咱們經常使用的put方法blog
public V put(K key, V value) { //是否須要初始化,區別在於put是不是第一次操做,若是是第一次操做將會初始化一個數組table出來 if (table == EMPTY_TABLE) { inflateTable(threshold); } //HashMap是能夠put key爲null的鍵值對的,此Entry將會放在table[0]所在的鏈表下,具體在鏈表的什麼位置只能看他是何時put進去的了,畢竟鏈表結構是先來得先排前面 if (key == null) return putForNullKey(value); //經過散列獲取hash int hash = hash(key); //經過hash和table長度肯定所屬鏈表 下標(分組) int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; //關於HashMap put如何肯定put的key重複 在此處作了邏輯判斷 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; }
有同窗不解了,如何經過hash和長度肯定所屬的鏈表(所屬的組,將鏈表看做一個組)內存
//經過hash和table長度肯定所屬鏈表 下標(分組) int i = indexFor(hash, table.length);
/** * Returns index for hash code h. */ static int indexFor(int h, int length) { // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2"; //經過與運算 返回了一個 0至length-1之間的int數據 return h & (length-1); }
接下來就是找好隊長去排隊,若是沒有隊長那麼我來擔任隊長rem
Entry<K,V> e = table[i]; e != null; e = e.next
待更!get