HashMap、ConcurrentHashMap

   一、定義

       HashMap實現了Map接口,繼承AbstractMap。其中Map接口定義了鍵映射到值的規則,而AbstractMap類提供 Map 接口的骨幹實現,以最大限度地減少實現此接口所需的工作,其實AbstractMap類已經實現了Map。

[java] view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. public class HashMap<K,V>  
  2.     extends AbstractMap<K,V>  
  3.     implements Map<K,V>, Cloneable, Serializable  

      二、構造函數

       HashMap提供了三個構造函數:

       HashMap():構造一個具有默認初始容量 (16) 和默認加載因子 (0.75) 的空 HashMap。

       HashMap(int initialCapacity):構造一個帶指定初始容量和默認加載因子 (0.75) 的空 HashMap。

       HashMap(int initialCapacity, float loadFactor):構造一個帶指定初始容量和加載因子的空 HashMap。

       在這裏提到了兩個參數:初始容量,加載因子。這兩個參數是影響HashMap性能的重要參數,其中容量表示哈希表中桶的數量,初始容量是創建哈希表時的容量,加載因子是哈希表在其容量自動增加之前可以達到多滿的一種尺度,它衡量的是一個散列表的空間的使用程度,負載因子越大表示散列表的裝填程度越高,反之愈小。對於使用鏈表法的散列表來說,查找一個元素的平均時間是O(1+a),因此如果負載因子越大,對空間的利用更充分,然而後果是查找效率的降低;如果負載因子太小,那麼散列表的數據將過於稀疏,對空間造成嚴重浪費。系統默認負載因子爲0.75,一般情況下我們是無需修改的。

       HashMap是一種支持快速存取的數據結構,要了解它的性能必須要了解它的數據結構。

 我們知道在Java中最常用的兩種結構是數組和模擬指針(引用),幾乎所有的數據結構都可以利用這兩種來組合實現,HashMap也是如此。實際上HashMap是一個“鏈表散列”,如下是它數據結構:


從上圖我們可以看出HashMap底層實現還是數組,只是數組的每一項都是一條鏈。其中參數initialCapacity就代表了該數組的長度。

public V put(K key, V value) {
	if (table == EMPTY_TABLE) { //HashMap支持null的key
		inflateTable(threshold);
	}
	if (key == null)
		return putForNullKey(value);
	int hash = hash(key);
	int i = indexFor(hash, table.length);//搜索指定Hash值在對應table中的索引
	for (Entry<K,V> e = table[i]; e != null; e = e.next) {//在i索引處Entry不爲空,循環遍歷e的下一個元素  
		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;
}
從數組(通過hash值)取得鏈表頭,然後通過equals比較key,如果相同,就覆蓋老的值,並返回老的值。(該key在hashmap中已存在)
否則新增一個entry,返回null。新增的元素爲鏈表頭,以前相同數組位置的掛在後面。
另外:modCount是爲了避免讀取一批數據時,在循環讀取的過程中發生了修改,就拋異常
  if (modCount != expectedModCount)
                throw new ConcurrentModificationException();

ConcurrentHashMap:

在hashMap的基礎上,ConcurrentHashMap將數據分爲多個segment,默認16個(concurrency level),然後每次操作對一個segment加鎖,避免多線程鎖得機率,提高併發效率。

public V put(K key, V value) {
        Segment<K,V> s;
        if (value == null)
            throw new NullPointerException();
        int hash = hash(key);
        int j = (hash >>> segmentShift) & segmentMask;
        if ((s = (Segment<K,V>)UNSAFE.getObject          // nonvolatile; recheck
             (segments, (j << SSHIFT) + SBASE)) == null) //  in ensureSegment
            s = ensureSegment(j);
        return s.put(key, hash, value, false);
    }
segment中的put方法

final V put(K key, int hash, V value, boolean onlyIfAbsent) {
	HashEntry<K,V> node = tryLock() ? null :
		scanAndLockForPut(key, hash, value);
	V oldValue;
	try {
		HashEntry<K,V>[] tab = table;
		int index = (tab.length - 1) & hash;
		HashEntry<K,V> first = entryAt(tab, index);
		for (HashEntry<K,V> e = first;;) {
			if (e != null) {
				K k;
				if ((k = e.key) == key ||
					(e.hash == hash && key.equals(k))) {
					oldValue = e.value;
					if (!onlyIfAbsent) {
						e.value = value;
						++modCount;
					}
					break;
				}
				e = e.next;
			}
			else {
				if (node != null)
					node.setNext(first);
				else
					node = new HashEntry<K,V>(hash, key, value, first);
				int c = count + 1;
				if (c > threshold && tab.length < MAXIMUM_CAPACITY)
					rehash(node);
				else
					setEntryAt(tab, index, node);
				++modCount;
				count = c;
				oldValue = null;
				break;
			}
		}
	} finally {
		unlock();
	}
	return oldValue;
}
參考:http://blog.csdn.net/zldeng19840111/article/details/6703104
參考:http://blog.csdn.net/chenssy/article/details/18323767
發佈了217 篇原創文章 · 獲贊 39 · 訪問量 54萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章