java 中 HashMap 的理解

    之前只會簡單的應用,具體的原理不怎麼懂,最近一直在看,總算懂了點,如今把個人感悟寫出來,但願能夠幫到他人.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();
    }

 

寫了這麼點,感受不少懂的東西可是沒法表達出來,真心佩服那些大牛.不但本身懂了,寫出來的東西也能讓別人懂.

但願個人這篇博客能夠給人幫助.若是哪裏寫的很差,請指點,謝謝.

相關文章
相關標籤/搜索