之前只會簡單的應用,具體的原理不怎麼懂,最近一直在看,總算懂了點,如今把個人感悟寫出來,但願能夠幫到他人.java
首先,HashMap 是數組與鏈表的結合體.空構造的狀況下,看下圖:數組
這個 table[] 數組就是你用來存放的, 一個 Entry<K,V> 表明一組 key-value 組合.函數
看put方法的源碼:性能
public V put(K key, V value) { if (table == EMPTY_TABLE) { inflateTable(threshold); } if (key == null) return putForNullKey(value);//若是key爲null,調用這個方法 int hash = hash(key);//計算key的hash值 int i = indexFor(hash, table.length);//根據hash值獲得數組下標 //遍歷開始 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))) {//Entry鏈表中,key值已經存在了 V oldValue = e.value; e.value = value;//新的value值覆蓋舊的value e.recordAccess(this); return oldValue; } } modCount++; addEntry(hash, key, value, i);//key不存在,須要新增一個 return null; }
看了源碼就能知道,key-value中,起決定做用的是key,value就只有在存值的時候有點用,table下標和是否已經有這個key,都要靠key來肯定.this
先判斷key是否爲空,若是爲空,執行putForNullKey.code
若是不爲空,再計算key的hashCode肯定table數組的下標,獲得Entry鏈表,接着開始遍歷.如今就要用到 equals 方法了.若是hashCode相等,而且 ==成立 或者 equals成立,那就說明,這個key,之前已經存在了,須要用如今的value覆蓋之前的value.ci
Entry鏈表的證實:get
因爲本人不知道如何改寫String類的 hashCode 方法和 equals 方法,沒法放到同一個下標下,但我能夠重寫一個類,裏面再重寫這兩個方法.以下:源碼
public class Person { private String name; public Person(String name){ this.name=name; } /**重寫 hashCode 方法**/ public int hashCode(){ return 1; } /**重寫 equals 方法**/ public boolean equals(){ return false; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
public static void main(String[] args) { Map<Person,Integer> map=new HashMap<Person,Integer>(); Person xiao=new Person("xiaofeiji"); Person da=new Person("dabaicai"); map.put(xiao, 1); map.put(da, 4); System.out.println(map.keySet()); }
因爲hashCode方法返回值都是1,因此獲得的數組下標同樣,也就是說放在同一個鏈表中博客
把數組展開:
固然實際中是不可能這個寫的,咱們要作的就是讓數據分散開來,越均勻越好,要是存放在一個鏈表中,浪費空間,也嚴重影響查詢性能.
以上是不須要擴容的狀況下(數據少),若是數據量很大,數組長度仍是16,那樣鏈表也會很長,也是很差的,這會就須要擴容了.即數組長度翻倍.
若是是空構造,在HashMap中,threshold=12(臨界值),loadFactor=0.75(負載因子).
threshold=table數組長度 * loadFactor.一開始的table長度爲16,因此臨界值爲12.這表明什麼呢?表明着,若是放入12個key不一樣的鍵值對,table數組是不會擴容的,當放入第13個,數組長度就會翻倍.
下面的構造函數中,直接給了 臨界值與加載因子
public HashMap(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal load factor: " + loadFactor); this.loadFactor = loadFactor; threshold = initialCapacity; init(); }
寫了這麼點,感受不少懂的東西可是沒法表達出來,真心佩服那些大牛.不但本身懂了,寫出來的東西也能讓別人懂.
但願個人這篇博客能夠給人幫助.若是哪裏寫的很差,請指點,謝謝.