1. 程式人生 > >java中HashMap、CurrentHashMap 工作原理&&和HashTable、HashSet的區別

java中HashMap、CurrentHashMap 工作原理&&和HashTable、HashSet的區別

HashMap和HashTable的區別

HashMap儲存的是鍵值對(接受null鍵值對),不支援synchronized,速度很快;
HashTable不接受null鍵值對,可同步(Synchronized)

雖然HashMap是非Synchronized,但collection框架提供方法能保證HashMap synchronized,這樣多個執行緒同時訪問HashMap時,能保證只有一個執行緒更改Map。

另一個區別是HashMap的迭代器(Iterator)fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以當有其它執行緒改變了HashMap的結構(增加或者移除元素),將會丟擲ConcurrentModificationException,但迭代器本身的remove()方法移除元素則不會丟擲ConcurrentModificationException異常。但這並不是一個一定發生的行為,要看JVM。這條同樣也是Enumeration和Iterator的區別。

HashMap不能保證隨著時間的推移Map中的元素次序是不變的。

HashMap的工作原理

“HashMap是基於hashing的原理,我們使用put(key, value)儲存物件到HashMap中,使用get(key)從HashMap中獲取物件。當我們給put()方法傳遞鍵和值時,我們先對鍵呼叫hashCode()方法,返回的hashCode用於找到bucket位置來儲存Entry物件。”
hashCode方法是返回地址,對應Entry物件。
也就是說HashMap是在bucket中儲存鍵和值的物件,而不僅僅是鍵值對。

HashMap中的碰撞:

當兩個物件的hashcode相同會發生什麼?


因為hashcode相同,所以它們的bucket位置相同,‘碰撞’會發生,HashMap將會丟擲異常,或者不會儲存它們。
(即使兩個物件不一定是equal)因為HashMap使用連結串列儲存物件,這個Entry(包含有鍵值對的Map.Entry物件)會儲存在連結串列中。

“如果兩個鍵的hashcode相同,你如何獲取值物件?”

當我們呼叫get()方法,HashMap會使用鍵物件的hashcode(key.hashcode())找到bucket位置,然後獲取值(value)物件。
如果有兩個值物件儲存在同一個bucket,將會遍歷連結串列直到找到值物件。也就是找到bucket位置之後,遍歷,呼叫keys.equals()方法去找到連結串列中正確的節點,最終找到要找的值物件。

**使用不可變的、宣告作final的物件,並且採用合適的equals()和hashCode()方法的話,將會減少碰撞的發生,提高效率。**不可變性使得能夠快取不同鍵的hashcode,這將提高整個獲取物件的速度,使用String,Interger這樣的wrapper類作為鍵是非常好的選擇。

如果HashMap的大小超過了負載因子(load factor)定義的容量,怎麼辦?

預設的負載因子大小為0.75,也就是說,當一個map填滿了75%的bucket時候,和其它集合類(如ArrayList等)一樣,將會建立原來HashMap大小的兩倍的bucket陣列,來重新調整map的大小,並將原來的物件放入新的bucket陣列中。這個過程叫作rehashing,因為它呼叫hash方法找到新的bucket位置。

重新調整HashMap大小存在什麼問題??

當多執行緒的情況下,可能產生條件競爭(race condition)

當重新調整HashMap大小的時候,確實存在條件競爭,因為如果兩個執行緒都發現HashMap需要重新調整大小了,它們會同時試著調整大小。在調整大小的過程中,儲存在連結串列中的元素的次序會反過來,因為移動到新的bucket位置的時候,HashMap並不會將元素放在連結串列的尾部,而是放在頭部,這是為了避免尾部遍歷(tail traversing)。如果條件競爭發生了,那麼就死迴圈了。這個時候,你可以質問面試官,為什麼這麼奇怪,要在多執行緒的環境下使用HashMap呢?:)

不可變性

為什麼String, Interger這樣的wrapper類適合作為鍵?

String最為常用。因為String是不可變的,也是final的,而且已經重寫了equals()和hashCode()方法了。
其他的wrapper類(Interger)也有這個特點。不可變性是必要的,因為為了要計算hashCode(),就要防止鍵值改變,如果鍵值在放入時和獲取時返回不同的hashcode的話,那麼就不能從HashMap中找到你想要的物件。

不可變性還有其他的優點如執行緒安全。如果你可以僅僅通過將某個field宣告成final就能保證hashCode是不變的,那麼請這麼做吧。因為獲取物件的時候要用到equals()和hashCode()方法,那麼鍵物件正確的重寫這兩個方法是非常重要的。如果兩個不相等的物件返回不同的hashcode的話,那麼碰撞的機率就會小些,這樣就能提高HashMap的效能。(減小碰撞機率,提高HashMap效能

我們可以使用自定義的物件作為鍵嗎?
你可能使用任何物件作為鍵,只要它遵守了equals()和hashCode()方法的定義規則,並且當物件插入到Map中之後將不會再改變了。如果這個自定義物件時不可變的,那麼它已經滿足了作為鍵的條件,因為當它建立之後就已經不能改變了。

我們可以使用CocurrentHashMap來代替Hashtable嗎?
ConcurrentHashMap是java5提供的HashTable的替代,但是擴充套件性更好。
我們知道Hashtable是synchronized的,但是ConcurrentHashMap同步效能更好,因為它僅僅根據同步級別對map的一部分進行上鎖**。ConcurrentHashMap當然可以代替HashTable,但是HashTable提供更強的執行緒安全性。**

部分轉載自:http://www.importnew.com/7099.html

HashMap和HashTable的區別:http://www.importnew.com/7010.html

HashMap和HashSet的區別

Java集合大致可分為Set、List和Map三種體系,其中Set代表無序、不可重複的集合;List代表有序、重複的集合;而Map則代表具有對映關係的集合。Java 5之後,增加了Queue體系集合,代表一種佇列集合實現。

Java集合框架主要由Collection(List Set Queue)和Map兩個根介面及其子介面、實現類組成。
在這裡插入圖片描述
Map介面有兩個基本的實現,HashMap和TreeMap。TreeMap儲存了物件的排列次序,HashMap不可以。

HashSet類是Set介面的典型實現類。
特點:
1.不能保證元素的排列順序,加入的元素要特別注意hashCode()方法的實現。
2.HashSet不是同步的,多執行緒訪問同一步HashSet物件時,需要手工同步。
3.集合元素值可以是null。
在這裡插入圖片描述

ConcurrentHashMap 實現原理:鎖分段技術

ConcurrentHashMap所使用的鎖分段技術,首先將資料分成一段一段的儲存,然後給每一段資料配一把鎖,當一個執行緒佔用鎖訪問其中一個段資料的時候,其他段的資料也能被其他執行緒訪問。有些方法需要跨段,比如size()和containsValue(),它們可能需要鎖定整個表而而不僅僅是某個段,這需要按順序鎖定所有段,操作完畢後,又按順序釋放所有段的鎖。這裡“按順序”是很重要的,否則極有可能出現死鎖,在ConcurrentHashMap內部,段陣列是final的,並且其成員變數實際上也是final的,但是,僅僅是將陣列宣告為final的並不保證陣列成員也是final的,這需要實現上的保證。這可以確保不會出現死鎖,因為獲得鎖的順序是固定的。

ConcurrentHashMap是由Segment陣列結構和HashEntry陣列結構組成。
Segment是一種可重入鎖ReentrantLock,在ConcurrentHashMap裡扮演鎖的角色,HashEntry則用於儲存鍵值對資料。
一個ConcurrentHashMap裡包含一個Segment陣列,Segment的結構和HashMap類似,是一種陣列和連結串列結構, 一個Segment裡包含一個HashEntry陣列,每個HashEntry是一個連結串列結構的元素, 每個Segment守護著一個HashEntry數組裡的元素,當對HashEntry陣列的資料進行修改時,必須首先獲得它對應的Segment鎖。

與HashMap不同的是,ConcurrentHashMap使用多個子Hash表,也就是段(Segment)。

ConcurrentHashMap–>一個Segment陣列(多個Segment段)
一個Segment段–>一個HashEntry陣列

CurrentHashMap應用場景

當有一個大陣列時需要在多個執行緒共享時就可以考慮是否把它給分層多個節點了,避免大鎖。並可以考慮通過hash演算法進行一些模組定位。
其實不止用於執行緒,當設計資料表的事務時(事務某種意義上也是同步機制的體現),可以把一個表看成一個需要同步的陣列,如果操作的表資料太多時就可以考慮事務分離了(這也是為什麼要避免大表的出現),比如把資料進行欄位拆分,水平分表等。

CurrentHashMap的實現原理:http://www.cnblogs.com/ITtangtang/p/3948786.html