淺析Hashmap原始碼
屌絲程式設計師的奮鬥之路現在開始
java集合這一塊無論在面試或在寫程式碼中,我們都會接觸到,所以java集合是特別重要的,其中HashMap更是被我們經常用到。
一.概括
HashMap是用鍵值對的既已key-value的形式來儲存值的,當然這只是展現給大家的一種表象,key和value都可以為空,但是key不能重複,HashMap不是現線安全的,如果想讓HashMap變成現線安全的,可以呼叫Collections的靜態方法synchronized方法。其實HashMap是用一個動態陣列和多個連結串列來存放key-value的
二.HashMap的資料結構
HashMap可以說是由一個動態陣列和多個連結串列組成,連結串列是接在每一個數組單元下面的,動態陣列和連結串列中儲存的單元是一個叫Entry的物件,從下面的圖中可以很直觀的看出HashMap的資料結構,其中每一個單元格儲存的就是Entry物件了,這一個Entry物件是HashMap的一個靜態類
Entry原始碼
[java] view plain copy- static class Entry<K,V> implements Map.Entry<K,V> {
- final K key;
- V value;
- Entry<K,V> next;//指向一下個Entry物件,他是為解決hash衝突而存在的。
- int hash;
- /**
- * 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 (key==null ? 0 : key.hashCode()) ^
- (value==null ? 0 : value.hashCode());
- }
- 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) {
- }
- }
從Entry的屬性中看到了我們所熟悉的key和value,沒錯,這就是我們在用HashMap的時候所要接觸到的key,value,Entry對key-value進行了封裝,我們再看看Enrty的next屬性,儲存的就是指向下一個物件的指標,當然java是沒有指標這一說的,我覺得在這裡將它當成指標更好理解,next在出現hash衝突的時候會發生作用,現在我們再看看上面的那一張圖,現在知道為什麼那些綠色的連結串列是怎麼連線起來的了吧,就是通過Entry的next屬性指向下一個Entry物件連線起來的,所以在HashMap原始碼中是看不到動態連結串列的定義,但是它確實是存在的。
三.HashMap的API
1.HashMap的相關屬性
[java] view plain copy- /**
- * HashMap中陣列的預設大小是16
- */
- static final int DEFAULT_INITIAL_CAPACITY = 16;
- /**
- * 陣列的最大長度
- */
- static final int MAXIMUM_CAPACITY = 1 << 30;
- /**
- * 預設的載入因子是0.75
- */
- static final float DEFAULT_LOAD_FACTOR = 0.75f;
- /**
- * 存放Entry物件的陣列,也是HashMap存放資料的地方
- */
- transient Entry<K,V>[] table;
- /**
- * HashMap的存入值得個數,注意:他和陣列的大小是沒有關係的
- */
- transient int size;
- /**
- * 邊界值 <span style="font-family: Arial, Helvetica, sans-serif;">邊界值=HahsMap的容量*載入因子</span>
- * @serial
- */
- int threshold;
- /**
- *載入因子
- * @serial
- */
- final float loadFactor;
邊界值=陣列大小*載入因子
當HashMap所儲存物件的個數超過邊界值的時候就會對陣列進行擴容,例如HashMap預設的載入因子是0.75,陣列預設的大小是16,所以邊界值是12,當我們在HashMap中儲存的值大於等於12的時候,HashMap會對陣列table進行2倍的擴容。
2.HashMap的構造方法
[java] view plain copy- /**
- *給陣列設定初始容量和載入因子
- */
- 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);